using System;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.IO;
using Windows.ApplicationModel.Store;
using Windows.System;
using Windows.Storage;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using System.Runtime.Serialization;
using FooEditEngine.Metro;
using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.Resources;
using Windows.Graphics.Printing;
using Windows.Storage.Pickers;
using Windows.Storage.AccessCache;
using Windows.UI.Popups;

namespace FooEditor
{
    public struct DocumentPageInfo : IXmlSerializable
    {
        public DocumentControl doc;
        public string Title
        {
            get
            {
                return doc.Title;
            }
        }
        public DocumentPageInfo(DocumentControl doc)
        {
            this.doc = doc;
        }

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            reader.ReadStartElement("DocumentPageInfo");
            this.doc = new DocumentControl();
            this.doc.ReadXml(reader);
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteStartElement("DocumentControl");
            this.doc.WriteXml(writer);
            writer.WriteEndElement();
        }
    }
    class MainPageViewModel : ViewModelBase
    {
        Frame Frame;
        DispatcherTimer timer = new DispatcherTimer();

        public MainPageViewModel(Frame frame)
        {
            this.Frame = frame;
        }

        public async Task NavigateHomePage(Dictionary<String, Object> pageState)
        {
            Window.Current.CoreWindow.KeyUp += CoreWindow_KeyUp;
            DataTransferManager.GetForCurrentView().DataRequested += DocumentPage_DataRequested;
            PrintManager.GetForCurrentView().PrintTaskRequested += DocumentPage_PrintTaskRequested;

            try
            {
                StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync("DocumentCollection.xml");
                using (Stream fs = await file.OpenStreamForReadAsync())
                {
                    DataContractSerializer serializer = new DataContractSerializer(typeof(DocumentCollection));
                    this.DocumentCollection = (DocumentCollection)serializer.ReadObject(fs);
                    this.DocumentCollection.ActiveDocumentChanged += DocumentCollection_ActiveDocumentChanged;
                }
                await file.DeleteAsync();
                if (pageState != null && pageState.Count > 0)
                {
                    int selIndex = (int)pageState["ActiveIndex"];
                    if (selIndex != -1)
                    {
                        this.DocumentCollection.ActiveDocument(selIndex);
                        return;
                    }
                }
            }
            catch (FileNotFoundException)
            {
                this.DocumentCollection = new DocumentCollection();
                this.DocumentCollection.ActiveDocumentChanged += DocumentCollection_ActiveDocumentChanged;
            }
            this.Frame.Navigate(typeof(HomePage), this.DocumentCollection);
            this.timer.Tick += Timer_Tick;
            this.timer.Interval = new TimeSpan(0, 0, 0, 0, TimerTick);
            this.timer.Start();
        }

        private void DocumentCollection_ActiveDocumentChanged(object sender, DocumentCollectionEventArgs e)
        {
            if (this.DocumentCollection.Current.Equals(DocumentCollection.Empty))
            {
                this.CurrentDocument = null; ;
                return;
            }
            this.CurrentDocument = this.DocumentCollection.Current.doc;
            this.Frame.Content = this.DocumentCollection.Current.doc;
            this.DocumentCollection.Current.doc.ApplySetting(AppSettings.Current);
            this.CurrentDocumentIndex = e.ActiveIndex;
        }

        public void LeaveMainPage(Dictionary<String, Object> pageState)
        {
            DataTransferManager.GetForCurrentView().DataRequested -= DocumentPage_DataRequested;

            PrintManager.GetForCurrentView().PrintTaskRequested -= DocumentPage_PrintTaskRequested;

            //DocumentUpdateの段階でDocumentCollectionを保存しているので、保存されているドキュメントの数と一致しないことがある
            if(this.CurrentDocumentIndex < this.DocumentCollection.Count)
              pageState["ActiveIndex"] = this.CurrentDocumentIndex;
        }

        void CoreWindow_KeyUp(CoreWindow sender, KeyEventArgs e)
        {
            bool isCtrlPressed = WindowUtils.IsModiferKeyPressed(VirtualKey.Control);
            bool isShiftPressed = WindowUtils.IsModiferKeyPressed(VirtualKey.Shift);
            if (isCtrlPressed)
            {
                switch (e.VirtualKey)
                {
                    case VirtualKey.N:
                        this.AddCommand.Execute(null);
                        break;
                    case VirtualKey.Tab:
                        if (isShiftPressed)
                            this.DocumentCollection.Prev();
                        else
                            this.DocumentCollection.Next();
                        break;
                    case VirtualKey.F:
                        this.ShowSearchFlyoutCommand.Execute(null);
                        break;
                    case VirtualKey.G:
                        this.ShowGoToFlyoutCommand.Execute(null);
                        break;
                    case VirtualKey.S:
                        this.SaveCommand.Execute(null);
                        break;
                    case VirtualKey.O:
                        this.OpenAsCommand.Execute(null);
                        break;
                }
            }
        }

        int _CurrentDocumentIndex;
        public int CurrentDocumentIndex
        {
            get
            {
                return this._CurrentDocumentIndex;
            }
            set
            {
                this._CurrentDocumentIndex = value;
                this.OnPropertyChanged();
            }
        }

        bool _TopAppBarOpen;
        public bool TopAppBarOpen
        {
            get
            {
                return this._TopAppBarOpen;
            }
            set
            {
                this._TopAppBarOpen = value;
                this.OnPropertyChanged();
            }
        }

        bool _BottomAppBarOpen;
        public bool BottomAppBarOpen
        {
            get
            {
                return this._BottomAppBarOpen;
            }
            set
            {
                this._BottomAppBarOpen = value;
                this.OnPropertyChanged();
            }
        }

        DocumentCollection _DocumentCollection;
        public DocumentCollection DocumentCollection
        {
            get
            {
                return this._DocumentCollection;
            }
            private set
            {
                this._DocumentCollection = value;
                this.OnPropertyChanged();
            }
        }

        DocumentControl CurrentDocument;

        public bool IsAppBarClosed()
        {
            return this.CurrentDocument == null;
        }

        void DocumentPage_PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args)
        {
            if (this.CurrentDocument == null)
                return;
            this.CurrentDocument.Print(args);
        }

        void DocumentPage_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
        {
            if (this.CurrentDocument == null)
                return;
            this.CurrentDocument.GetData(args);
        }

        public DelegateCommand<object> OpenAsCommand
        {
            get
            {
                return new DelegateCommand<object>(async (param) =>
                {
                    await DocumentCollectionHelper.AddFromFilePicker(this.DocumentCollection);
                });
            }
        }

        public DelegateCommand<object> AddCommand
        {
            get
            {
                return new DelegateCommand<object>((param) =>
                {
                    this.Frame.Navigate(typeof(HomePage), this.DocumentCollection);
                    this.TopAppBarOpen = false;
                    this.BottomAppBarOpen = false;
                    this.CurrentDocument = null;
                });
            }
        }

        public DelegateCommand<DocumentPageInfo> RemoveCommand
        {
            get
            {
                return new DelegateCommand<DocumentPageInfo>(async (param) =>
                {
                    if(param.doc.IsDirty)
                    {
                        ResourceLoader loader = new ResourceLoader();
                        string message = string.Format(loader.GetString("DiscardDocumentMessage"), param.Title);
                        MessageDialog dialog = new MessageDialog(message);
                        dialog.Commands.Add(new UICommand(loader.GetString("YesButton"), (p) =>
                        {
                            RemoveDocumentPage(param);
                        }));
                        dialog.Commands.Add(new UICommand(loader.GetString("NoButton")));
                        dialog.DefaultCommandIndex = 1;
                        await dialog.ShowAsync();
                    }
                    else
                    {
                        RemoveDocumentPage(param);
                    }
                    this.TopAppBarOpen = false;
                    this.BottomAppBarOpen = false;
                });
            }
        }

        void RemoveDocumentPage(DocumentPageInfo param)
        {
            this.DocumentCollection.RemoveDocument(param);
            if (this.DocumentCollection.Count == 0)
            {
                this.Frame.Navigate(typeof(HomePage), this.DocumentCollection);
                this.CurrentDocument = null;
            }
        }

        public DelegateCommand<object> UndoCommand
        {
            get
            {
                return new DelegateCommand<object>((param) =>
                {
                    this.CurrentDocument.Undo();
                });
            }
        }

        public DelegateCommand<object> RedoCommand
        {
            get
            {
                return new DelegateCommand<object>((param) =>
                {
                    this.CurrentDocument.Redo();
                });
            }
        }

        public DelegateCommand<object> SaveCommand
        {
            get
            {
                return new DelegateCommand<object>(async (param) =>
                {
                    FileSavePicker savePicker = new FileSavePicker();
                    savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
                    ObservableCollection<FileType> collection = AppSettings.Current.FileTypeCollection;
                    foreach (FileType type in collection)
                        savePicker.FileTypeChoices.Add(type.DocumentTypeName, type.ExtensionCollection);
                    savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
                    StorageFile file = await savePicker.PickSaveFileAsync();
                    if (file != null)
                    {
                        await this.CurrentDocument.SaveFile(new FileModel(file));
                    }
                });
            }
        }

        public DelegateCommand<object> ShowSearchFlyoutCommand
        {
            get
            {
                return new DelegateCommand<object>((param) =>
                {
                    var flyout = new FindFlyout(this);
                    var popup = FlyoutUtils.CreateFlyoutUnderTopAppBar(flyout);
                    popup.IsOpen = true;
                    CloseAllAppBar();
                });
            }
        }

        public DelegateCommand<object> ShowGoToFlyoutCommand
        {
            get
            {
                return new DelegateCommand<object>((param) =>
                {
                    var popup = this.CurrentDocument.CreatePopup(typeof(GoToFlyout));
                    popup.IsOpen = true;
                    CloseAllAppBar();
                });
            }
        }

        public DelegateCommand<object> ShowPropertyFlyoutCommand
        {
            get
            {
                return new DelegateCommand<object>((param) =>
                {
                    var flyout = new PropertyFlyout(this.CurrentDocument);
                    var popup = FlyoutUtils.CreateFlyoutUnderTopAppBar(flyout);
                    popup.IsOpen = true;
                    CloseAllAppBar();
                });
            }
        }


        //オートセーブの間隔
        const int AutoSaveTick = 10000 * 1000 * 1;
        //インターバルタイマーの分解能
        const int TimerTick = 500;

        long lastSaveTick = 0;

        async void Timer_Tick(object sender, object e)
        {
            long nowTick = DateTime.Now.Ticks;
            //一定時間ドキュメントが更新されてなければ保存する
            if (nowTick - lastSaveTick >= AutoSaveTick)
            {
                this.timer.Stop();
                StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync("DocumentCollection.xml", CreationCollisionOption.ReplaceExisting);
                using (Stream fs = await file.OpenStreamForWriteAsync())
                {
                    this.DocumentCollection.SaveDocumentCollection(fs);
                }
                this.timer.Start();
            }
        }

        public async Task CreateDocuments(IReadOnlyCollection<IStorageItem> files)
        {
            foreach(StorageFile file in files)
            {
                await DocumentCollectionHelper.AddFromFile(this.DocumentCollection, file);
            }
        }

        private void CloseAllAppBar()
        {
            this.TopAppBarOpen = false;
            this.BottomAppBarOpen = false;
        }
    }
}
