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;
        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(ObservableCollection<DocumentPageInfo>));
                    this.DocumentCollection = (ObservableCollection<DocumentPageInfo>)serializer.ReadObject(fs);
                }
                await file.DeleteAsync();
                if (pageState != null && pageState.Count > 0)
                {
                    int selIndex = (int)pageState["ActiveIndex"];
                    if (selIndex != -1)
                    {
                        this.ActiveDocument(selIndex);
                        return;
                    }
                }
            }
            catch (FileNotFoundException)
            {
                this.DocumentCollection = new ObservableCollection<DocumentPageInfo>();
            }
            this.Frame.Navigate(typeof(HomePage), this);
        }

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

            PrintManager.GetForCurrentView().PrintTaskRequested -= DocumentPage_PrintTaskRequested;

            pageState["ActiveIndex"] = this.CurrentDocumentIndex;
            DataContractSerializer serializer = new DataContractSerializer(typeof(ObservableCollection<DocumentPageInfo>));
            StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync("DocumentCollection.xml", CreationCollisionOption.ReplaceExisting);
            using (Stream fs = await file.OpenStreamForWriteAsync())
            {
                serializer.WriteObject(fs, this.DocumentCollection);
            }
        }

        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:
                        int selIndex = isShiftPressed ? this.CurrentDocumentIndex - 1 : this.CurrentDocumentIndex + 1;
                        if (selIndex > this.DocumentCollection.Count - 1)
                            selIndex = 0;
                        else if (selIndex < 0)
                            selIndex = this.DocumentCollection.Count - 1;
                        this.ActiveDocument(selIndex);
                        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 = -1;
        public int CurrentDocumentIndex
        {
            get
            {
                return this._CurrentDocumentIndex;
            }
            set
            {
                this._CurrentDocumentIndex = value;
                this.OnPropertyChanged();
            }
        }

        bool _AppBarOpen;
        public bool AppBarOpen
        {
            get
            {
                return this._AppBarOpen;
            }
            set
            {
                this._AppBarOpen = value;
                this.OnPropertyChanged();
            }
        }

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

        DocumentControl CurrentDocument;

        public bool IsAppBarClosed()
        {
            return this.CurrentDocument == null || this.CurrentDocument.IsHitTextArea(Window.Current.CoreWindow.PointerPosition);
        }

        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) =>
                {
                    FileOpenPicker openPicker = new FileOpenPicker();

                    openPicker.ViewMode = PickerViewMode.List;

                    openPicker.FileTypeFilter.Add("*");

                    openPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
                    StorageFile file = await openPicker.PickSingleFileAsync();
                    if (file != null)
                    {
                        await this.CreateDocument(file);
                    }
                });
            }
        }

        public DelegateCommand<object> AddCommand
        {
            get
            {
                return new DelegateCommand<object>((param) =>
                {
                    this.Frame.Navigate(typeof(HomePage), this);
                    this.AppBarOpen = 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.AppBarOpen = false;
                });
            }
        }

        void RemoveDocumentPage(DocumentPageInfo param)
        {
            param.doc.Dispose();
            int index = this.DocumentCollection.IndexOf(param);
            this.DocumentCollection.RemoveAt(index);
            if (this.DocumentCollection.Count == 0)
            {
                this.Frame.Navigate(typeof(HomePage), this);
                this.CurrentDocument = null;
            }
            else if (index != -1)
            {
                this.ActiveDocument(index == 0 ? 0 : index - 1);
            }
        }

        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(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();
                });
            }
        }


        public async Task CreateDocument(FileType doctype)
        {
            DocumentControl doc = new DocumentControl();

            var loader = new Windows.ApplicationModel.Resources.ResourceLoader();
            doc.Title = string.Format(loader.GetString("NewDocumentTitle"), this.DocumentCollection.Count);
            await doc.SetDocumentType(doctype);

            DocumentPageInfo info = new DocumentPageInfo(doc);
            this.DocumentCollection.Add(info);
            this.ActiveDocument(info);
        }

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

        public async Task CreateDocument(StorageFile file)
        {
            if (file != null)
            {
                DocumentControl doc = new DocumentControl();
                doc.Title = file.Name;

                DocumentPageInfo info = new DocumentPageInfo(doc);
                this.DocumentCollection.Add(info);
                this.ActiveDocument(info);

                await doc.LoadFile(file);
                StorageApplicationPermissions.MostRecentlyUsedList.Add(file, "mrufile");
            }
        }

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

        private void ActiveDocument(int index)
        {
            DocumentPageInfo info = this.DocumentCollection[index];
            info.doc.ApplySetting(AppSettings.Current);
            this.CurrentDocumentIndex = index;
            this.Frame.Content = info.doc;
            this.CurrentDocument = info.doc;
        }

        public void ActiveDocument(DocumentPageInfo info)
        {
            this.ActiveDocument(this.DocumentCollection.IndexOf(info));
        }
    }
}
