using Prism.Windows.Mvvm;
using Prism.Windows.Navigation;
using Prism.Commands;
using Microsoft.Practices.Unity;
using System.IO;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using FooEditor.UWP.Services;
using FooEditor.UWP.Models;
using System;
using System.Threading.Tasks;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
using EncodeDetect;
using Windows.Foundation;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.AccessCache;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Navigation;
using Windows.Graphics.Printing;
using Windows.System.Threading;

namespace FooEditor.UWP.ViewModels
{
    public class MainPageViewModel : ViewModelBase
    {
        INavigationService NavigationService;
        IMainViewService MainViewService;
        DispatcherTimer timer;
        bool IsRequierDelayCleanStatusMessage = false;

        [InjectionConstructor]
        public MainPageViewModel(INavigationService navigationService,IMainViewService mainViewService)
        {
            this.DocumentList = DocumentCollection.Instance;
            this.NavigationService = navigationService;
            this.MainViewService = mainViewService;
        }

        public override async void OnNavigatedTo(NavigatedToEventArgs e, Dictionary<string, object> viewModelState)
        {
            base.OnNavigatedTo(e, viewModelState);

            this.DocumentList.ActiveDocumentChanged += DocumentList_ActiveDocumentChanged;

            //復元する必要がある
            if(e.NavigationMode == NavigationMode.New || e.NavigationMode == NavigationMode.Refresh)
            {
                if (viewModelState != null && viewModelState.Count > 0)
                {
                    if (await this.MainViewService.ConfirmRestoreUserState())
                        await RestoreUserDocument(viewModelState);
                }
            }

            //初めて起動した場合
            if (e.NavigationMode == NavigationMode.New)
            {
                if(e.Parameter != null)
                {
                    ObjectToXmlConverter conv = new ObjectToXmlConverter();
                    var files = conv.ConvertBack(e.Parameter, typeof(string[]), null, null) as string[];
                    if (files != null)
                    {
                        foreach (string filepath in files)
                        {
                            var file = await FileModel.GetFileModelFromPath(filepath);
                            await this.DocumentList.AddFromFile(file.File);
                        }
                    }
                }
            }

            if (this.DocumentList.Count == 0)
                this.DocumentList.AddNewDocument();
            await this.OnLoadCategories();

            //前回保存したときのごみが残っていることがある
            await DocumentCollection.CleanUp();

            this.timer = new DispatcherTimer();
            this.timer.Interval = new TimeSpan(0, 0, DocumentCollection.TimerTickInterval);
            this.timer.Tick += Timer_Tick;
            this.timer.Start();
        }

        private async Task RestoreUserDocument(Dictionary<string, object> viewModelState)
        {
            FileModel file = await FileModel.TryGetFileModelFromAppSetting("DocumentCollection.xml");
            if(file != null)
            {
                await this.DocumentList.RestoreFrom(file);
                if (viewModelState != null && viewModelState.Count > 0)
                {
                    var selIndex = viewModelState["CurrentDocumentIndex"] as int?;
                    if (selIndex != null && selIndex < this.DocumentList.Count)
                        this.DocumentList.ActiveDocument(selIndex.Value);
                }
                await file.DeleteAsync();
                System.Diagnostics.Debug.WriteLine("restored previous document");
            }
        }

        private async void Timer_Tick(object sender, object e)
        {
            if(AppSettings.Current.EnableAutoSave)
                await this.DocumentList.SaveDocumentCollection();
            if(this.IsRequierDelayCleanStatusMessage)
            {
                this.StatusMessage = string.Empty;
                this.IsRequierDelayCleanStatusMessage = false;
            }
        }

        public override async void OnNavigatingFrom(NavigatingFromEventArgs e, Dictionary<string, object> viewModelState, bool suspending)
        {
            base.OnNavigatingFrom(e, viewModelState, suspending);

            if(suspending)
            {
                viewModelState["CurrentDocumentIndex"] = this.DocumentList.CurrentDocumentIndex;    //選択中のドキュメントは別途保存する必要がある

                await AppSettings.Current.Save();
            }

            //null以外の場合はパンドラーを外す必要がある
            if(e.SourcePageType != null)
            {
                this.DocumentList.ActiveDocumentChanged -= DocumentList_ActiveDocumentChanged;
                if (this.timer != null)
                {
                    this.timer.Stop();
                    this.timer.Tick -= Timer_Tick;
                    this.timer = null;
                }
            }
        }

        private void DocumentList_ActiveDocumentChanged(object sender, DocumentCollectionEventArgs e)
        {
            this.RaisePropertyChanged("CurrentDocument");
            this.RaisePropertyChanged("MaxRow");
            this.RaisePropertyChanged("DocumentType");
            this.RaisePropertyChanged("Encode");
            this.RaisePropertyChanged("LineFeed");
        }

        DocumentCollection _doc_list;
        public DocumentCollection DocumentList
        {
            get
            {
                return this._doc_list;
            }
            set
            {
                this._FindModel.DocumentCollection = value;
                SetProperty(ref this._doc_list, value);
            }
        }

        public DocumentInfoViewModel CurrentDocument
        {
            get
            {
                if (_doc_list == null)
                    return null;
                return this._doc_list.Current;
            }
            set
            {
                if (_doc_list == null)
                    return;
                this._doc_list.ActiveDocument(value);
                this.RaisePropertyChanged();
            }
        }

        public DelegateCommand<object> AddDocumentCommand
        {
            get
            {
                return new DelegateCommand<object>((s) => {
                    this._doc_list.AddNewDocument();
                });
            }
        }

        public DelegateCommand<object> RemoveDocumentCommand
        {
            get
            {
                return new DelegateCommand<object>((s) => {
                    if(this._doc_list.Count > 1)
                    {
                        DocumentInfoViewModel doc = s as DocumentInfoViewModel;
                        this._doc_list.RemoveDocument(doc);
                    }
                });
            }
        }

        public DelegateCommand<System.Text.Encoding> OpenFileCommand
        {
            get
            {
                return new DelegateCommand<System.Text.Encoding>(async (s) => {
                    await this._doc_list.AddFromFilePicker(s);
                });
            }
        }

        public DelegateCommand<IEnumerable<string>> OpenFilePathCommand
        {
            get
            {
                return new DelegateCommand<IEnumerable<string>>(async (s) => {
                    if (s == null)
                        return;
                    foreach(var file_path in s)
                    {
                        var file = await FileModel.GetFileModelFromPath(file_path);
                        await this.DocumentList.AddFromFile(file.File);
                    }
                });
            }
        }

        public DelegateCommand<object> SaveCommand
        {
            get
            {
                return new DelegateCommand<object>(async (s) => {
                    if (this._doc_list.Current.DocumentModel.CurrentFilePath == null)
                    {
                        await SaveAs(null);
                        return;
                    }
                    var fileModel = await FileModel.GetFileModelFromPath(this._doc_list.Current.DocumentModel.CurrentFilePath);
                    if (fileModel != null)
                    {
                        if (fileModel.IsNeedUserActionToSave())
                            await this.SaveAs(fileModel);
                        else
                        {
                            await this._doc_list.Current.DocumentModel.SaveFile(fileModel,null);
                            var loader = new Windows.ApplicationModel.Resources.ResourceLoader();
                            var str = string.Format(loader.GetString("NotifySaveCompleteText"), fileModel.Name);
                            this.StatusMessage = str;
                            this.IsRequierDelayCleanStatusMessage = true;
                        }
                    }
                });
            }
        }

        public DelegateCommand<System.Text.Encoding> SaveAsCommand
        {
            get
            {
                return new DelegateCommand<System.Text.Encoding>(async (enc) => {
                    await SaveAs(null, enc);
                });
            }
        }

        private async Task SaveAs(FileModel suggestFile, System.Text.Encoding enc = null)
        {
            FileSavePicker savePicker = new FileSavePicker();
            savePicker.FileTypeChoices.Add("Unknown", new List<string>() { "." });
            ObservableCollection<FileType> collection = AppSettings.Current.FileTypeCollection;
            foreach (FileType type in collection)
                savePicker.FileTypeChoices.Add(type.DocumentTypeName, type.ExtensionCollection);
            if (suggestFile == null)
                savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
            else
                savePicker.SuggestedSaveFile = suggestFile.File;
            StorageFile file = await savePicker.PickSaveFileAsync();
            if (file != null)
            {
                FileModel fileModel = new FileModel(file);
                await this._doc_list.Current.DocumentModel.SaveFile(fileModel,enc);
                this._doc_list.Current.Title = file.Name;
                if(enc != null)
                    this._doc_list.Current.Encode = enc;
                StorageApplicationPermissions.MostRecentlyUsedList.Add(file, file.Name);

                var loader = new Windows.ApplicationModel.Resources.ResourceLoader();
                var str = string.Format(loader.GetString("NotifySaveCompleteText"), file.Name);
                this.StatusMessage = str;
                this.IsRequierDelayCleanStatusMessage = true;
            }
        }

        public DelegateCommand<object> UndoCommand
        {
            get
            {
                return new DelegateCommand<object>((param) =>
                {
                    this._doc_list.Current.DocumentModel.Document.UndoManager.undo();
                    this._doc_list.Current.DocumentModel.Document.RequestRedraw();
                });
            }
        }

        public DelegateCommand<object> RedoCommand
        {
            get
            {
                return new DelegateCommand<object>((param) =>
                {
                    this._doc_list.Current.DocumentModel.Document.UndoManager.redo();
                    this._doc_list.Current.DocumentModel.Document.RequestRedraw();
                });
            }
        }

        public DelegateCommand<object> GlobalSettingCommand
        {
            get
            {
                return new DelegateCommand<object>((s) => {
                    NavigationService.Navigate("GlobalSetting", null);
                });
            }
        }

        public DelegateCommand<object> FileTypeSettingCommand
        {
            get
            {
                return new DelegateCommand<object>((s) => {
                    NavigationService.Navigate("FileTypes", null);
                });
            }
        }

        public DelegateCommand<object> PrintSettingCommand
        {
            get
            {
                return new DelegateCommand<object>((s) => {
                    NavigationService.Navigate("PrintSettings", null);
                });
            }
        }

        public DelegateCommand<object> AboutPageCommand
        {
            get
            {
                return new DelegateCommand<object>((s) => {
                    NavigationService.Navigate("About", null);
                });
            }
        }

        public DelegateCommand<object> PrintCommand
        {
            get
            {
                return new DelegateCommand<object>(async (s) => {
                    try
                    {
                        await PrintManager.ShowPrintUIAsync();
                    }
                    catch
                    {
                        System.Diagnostics.Debug.WriteLine("Printer is not stanby");
                    }
                });
            }
        }

        string _Result;
        public string Result
        {
            get
            {
                return this._Result;
            }
            set
            {
                SetProperty(ref this._Result, value);
            }
        }

        string _StatusMessage;
        public string StatusMessage
        {
            get
            {
                return this._StatusMessage;
            }
            set
            {
                SetProperty(ref this._StatusMessage, value);
            }
        }

        #region FindAndReplace
        FindModel _FindModel = new FindModel();

        string _FindPattern;
        public string FindPattern
        {
            get
            {
                return this._FindPattern;
            }
            set
            {
                this._FindPattern = value;
                this.RaisePropertyChanged();
            }
        }

        string _SelectedFindPattern;
        public string SelectedFindPattern
        {
            get
            {
                return this._SelectedFindPattern;
            }
            set
            {
                this._SelectedFindPattern = value;
                this.FindPattern = value;
                this.RaisePropertyChanged();
            }
        }

        ObservableCollection<string> _FindHistroy;
        public ObservableCollection<string> FindHistroy
        {
            get
            {
                return this._FindHistroy;
            }
            set
            {
                this._FindHistroy = value;
                this.RaisePropertyChanged();
            }
        }

        string _ReplacePattern;
        public string ReplacePattern
        {
            get
            {
                return this._ReplacePattern;
            }
            set
            {
                this._ReplacePattern = value;
                this.RaisePropertyChanged();
            }
        }

        bool _UseRegEx;
        public bool UseRegEx
        {
            get
            {
                return this._UseRegEx;
            }
            set
            {
                this._UseRegEx = value;
                this._FindModel.Reset();
                this.RaisePropertyChanged();
            }
        }

        bool _RestrictSearch;
        public bool RestrictSearch
        {
            get
            {
                return this._RestrictSearch;
            }
            set
            {
                this._RestrictSearch = value;
                this._FindModel.Reset();
                this.RaisePropertyChanged();
            }
        }

        bool _UseGroup;
        public bool UseGroup
        {
            get
            {
                return this._UseGroup;
            }
            set
            {
                this._UseGroup = value;
                this.RaisePropertyChanged();
            }
        }

        public bool AllDocuments
        {
            get
            {
                return this._FindModel.AllDocuments;
            }
            set
            {
                this._FindModel.AllDocuments = value;
                this.RaisePropertyChanged();
            }
        }

        public DelegateCommand<object> FindNextCommand
        {
            get
            {
                return new DelegateCommand<object>((s) =>
                {
                    this.Result = string.Empty;
                    try
                    {
                        this.AddFindHistory(this.FindPattern);

                        RegexOptions opt = this.RestrictSearch ? RegexOptions.None : RegexOptions.IgnoreCase;
                        this._FindModel.FindNext(this.FindPattern, this.UseRegEx, opt);
                    }
                    catch(NotFoundExepction)
                    {
                        var loader = new Windows.ApplicationModel.Resources.ResourceLoader();
                        this.Result = loader.GetString("NotFoundInDocument");
                    }
                    catch (Exception e)
                    {
                        this.Result = e.Message;
                    }
                });
            }
        }

        public DelegateCommand<object> ReplaceNextCommand
        {
            get
            {
                return new DelegateCommand<object>((s) =>
                {
                    this.Result = string.Empty;
                    try
                    {
                        this._FindModel.Replace(this.ReplacePattern, this.UseGroup);
                        RegexOptions opt = this.RestrictSearch ? RegexOptions.None : RegexOptions.IgnoreCase;
                        this._FindModel.FindNext(this.FindPattern, this.UseRegEx, opt);
                    }
                    catch (Exception e)
                    {
                        this.Result = e.Message;
                    }
                });
            }
        }

        public DelegateCommand<object> ReplaceAllCommand
        {
            get
            {
                return new DelegateCommand<object>((s) => {
                    this.Result = string.Empty;
                    try
                    {
                        this.AddFindHistory(this.FindPattern);

                        RegexOptions opt = this.RestrictSearch ? RegexOptions.None : RegexOptions.IgnoreCase;
                        this._FindModel.ReplaceAll(this.FindPattern, this.ReplacePattern, this.UseGroup, this.UseRegEx, opt);
                    }
                    catch (Exception e)
                    {
                        this.Result = e.Message;
                    }
                });
            }
        }

        void AddFindHistory(string pattern)
        {
            if (this.FindHistroy == null)
                return;
            if (!this.FindHistroy.Contains(this.FindPattern))
                this.FindHistroy.Add(this.FindPattern);
        }
        #endregion

        #region GoTo
        int _ToRow;
        public int ToRow
        {
            get
            {
                return this._ToRow;
            }
            set
            {
                this._ToRow = value;
                this.RaisePropertyChanged();
            }
        }

        public int MaxRow
        {
            get
            {
                return this._doc_list.Current.DocumentModel.Document.LayoutLines.Count;
            }
        }

        public DelegateCommand<object> JumpLineCommand
        {
            get
            {
                return new DelegateCommand<object>((s) =>
                {
                    var newPostion = new FooEditEngine.TextPoint() ;
                    newPostion.row = this.ToRow;
                    newPostion.col = 0;
                    var loader = new Windows.ApplicationModel.Resources.ResourceLoader();
                    if (this.ToRow > MaxRow)
                    {
                        this.Result = string.Format(loader.GetString("LineNumberOutOutOfRange"), 1, this.MaxRow);
                        return;
                    }
                    this._doc_list.Current.DocumentModel.Document.CaretPostion = newPostion;
                    this._doc_list.Current.DocumentModel.Document.RequestRedraw();
                });
            }
        }
        #endregion

        #region DocumentProperty
        public ObservableCollection<FileType> FileTypeCollection
        {
            get
            {
                return AppSettings.Current.FileTypeCollection;
            }
        }

        public ObservableCollection<Encoding> EncodeCollection
        {
            get
            {
                return AppSettings.SupportEncodeCollection;
            }
        }

        static ObservableCollection<LineFeedType> lineFeedCollection = new ObservableCollection<LineFeedType>() {
            LineFeedType.CR,
            LineFeedType.CRLF,
            LineFeedType.LF
        };
        public ObservableCollection<LineFeedType> LineFeedCollection
        {
            get
            {
                return lineFeedCollection;
            }
        }

        public FileType DocumentType
        {
            get
            {
                return this._doc_list.Current.DocumentModel.DocumentType;
            }
            set
            {
                var taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
                var task = this._doc_list.Current.DocumentModel.SetDocumentType(value);
                task.ContinueWith((s) => {
                    this.RaisePropertyChanged();
                },taskScheduler);
            }
        }

        #endregion

        #region Outline
        bool _IsOutlineOpen;
        public bool IsOutLineOpen
        {
            get
            {
                return this._IsOutlineOpen;
            }
            set
            {
                SetProperty(ref this._IsOutlineOpen, value);
            }
        }

        public DelegateCommand<object> OpenOutlineCommand
        {
            get
            {
                return new DelegateCommand<object>((s) => {
                    this.IsOutLineOpen = true;
                });
            }
        }
        #endregion

        #region Snippet
        ObservableCollection<SnippetCategory> _CategoryList;
        public ObservableCollection<SnippetCategory> CategoryList
        {
            get
            {
                return this._CategoryList;
            }
            set
            {
                SetProperty(ref _CategoryList, value);
            }
        }

        SnippetCategory _CurrentCategory;
        public SnippetCategory CurrentCategory
        {
            get
            {
                return this._CurrentCategory;
            }
            set
            {
                SetProperty(ref _CurrentCategory, value);
                this.OnChangeCategory();
            }
        }

        ObservableCollection<Snippet> _Snippets;
        public ObservableCollection<Snippet> Snippets
        {
            get
            {
                return this._Snippets;
            }
            set
            {
                SetProperty(ref this._Snippets, value);
            }
        }

        Snippet _SelectSnippet;
        public Snippet SelectSnippet
        {
            get
            {
                return this._SelectSnippet;
            }
            set
            {
                SetProperty(ref _SelectSnippet, value);
            }
        }

        private async Task OnLoadCategories()
        {
            this.CategoryList = await SnipeetModel.LoadCategory();
        }

        private async void OnChangeCategory()
        {
            this.Snippets = await SnipeetModel.LoadSnippets(this.CurrentCategory.FilePath);
        }

        public DelegateCommand<object> InsertSnippetCommand
        {
            get
            {
                return new DelegateCommand<object>((s) => {
                    var doc = this.DocumentList.Current.DocumentModel.Document;
                    int caretIndex = doc.LayoutLines.GetIndexFromTextPoint(doc.CaretPostion);
                    doc.Insert(caretIndex, this.SelectSnippet.Data);
                    doc.RequestRedraw();
                });
            }
        }
        #endregion
    }
}
