using System;
using System.Collections.Generic;
using System.Drawing;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;
using Microsoft.WindowsAPICodePack.Dialogs;
using WeifenLuo.WinFormsUI.Docking;
using FooEditor.Properties;
using FooEditEngine;
using FooEditEngine.Windows;
using EncodeDetect;

namespace FooEditor.Plugin
{
    public enum DockingType
    {
        DockLeft = DockState.DockLeft,
        DockRight = DockState.DockRight,
        DockTop = DockState.DockTop,
        DockBottom = DockState.DockBottom,
    }

    public class EditFormEventArgs : EventArgs
    {
        public EditForm edit;
        public EditFormEventArgs(EditForm e)
        {
            this.edit = e;
        }
    }

    public delegate void CreateEditFormEventHandler(object sender, EditFormEventArgs e);

    /// <summary>
    /// エディター自身を表す
    /// </summary>
    public class Editor
    {
        Config cfg;
        string lastOpenDirectory;

        internal Editor(DockPanel panel)
        {
            this.CreateingEditFormEvent +=new CreateEditFormEventHandler((s,e)=>{});
            this.CreatedEditFromEvent += new CreateEditFormEventHandler((s,e)=>{});
            this.cfg = Config.GetInstance();
            this.Panel = panel;
            this.Panel.ActiveContentChanged += new EventHandler(Panel_ActiveContentChanged);
            this.lastOpenDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            this.Forms = new FormCollection(panel);
        }

        internal DockPanel Panel
        {
            get;
            private set;
        }

        /// <summary>
        /// エディットフォームが作成される前に呼び出されるイベント
        /// </summary>
        public event CreateEditFormEventHandler CreateingEditFormEvent;

        /// <summary>
        /// エディットフォーム作成時に呼びされるイベント
        /// </summary>
        public event CreateEditFormEventHandler CreatedEditFromEvent;

        /// <summary>
        /// アクティブウィンドウが変更されたときに呼び出されるイベント
        /// </summary>
        public event EventHandler ActiveContentChanged;

        /// <summary>
        /// アクティブなエディットフォーム
        /// </summary>
        public EditForm EditForm
        {
            get;
            set;
        }

        public FormCollection Forms
        {
            get;
            private set;
        }

        /// <summary>
        /// アクティブなテキストボックス
        /// </summary>
        public FooTextBox TextBox
        {
            get;
            set;
        }

        /// <summary>
        /// メニューのルート項目
        /// </summary>
        public MenuStrip RootMenuStrip
        {
            get;
            set;
        }

        /// <summary>
        /// コンテキストメニューの項目
        /// </summary>
        public ContextMenuStrip ContextMenuStrip
        {
            get;
            set;
        }

        /// <summary>
        /// ドッキングフォームを表示する
        /// </summary>
        /// <param name="form">DockingFormオブジェクト</param>
        /// <param name="type">DockingType列挙体</param>
        public void Show(DockingForm form, DockingType type)
        {
            form.Show(this.Panel, (DockState)type);
        }

        /// <summary>
        /// 新しくウィンドウを作る
        /// </summary>
        /// <returns>EditFormオブジェクト</returns>
        public EditForm CreateNew()
        {
            EditForm NewFrom = new EditForm();
            this.CreateingEditFormEvent(this, new EditFormEventArgs(NewFrom));

            InitEditForm(NewFrom);
            NewFrom.fooTextBox1.InsertMode = true;
            NewFrom.linefeed = LineFeedType.CRLF;
            NewFrom.DocumentType = null;
            NewFrom.enc = Encoding.Default;
            NewFrom.Text = string.Format(Resources.NewEditoFormTitle, this.Panel.Contents.Count);

            NewFrom.Show(this.Panel, DockState.Document);

            this.CreatedEditFromEvent(this, new EditFormEventArgs(NewFrom));
            
            return NewFrom;
        }

        /// <summary>
        /// ファイルダイアログを開き、そこからファイルを読み取る
        /// </summary>
        /// <param name="active">読み取り先のEditForm</param>
        public void LoadFromDialog(EditForm active)
        {
            if (active == null)
                active = this.CreateNew();
            CustomFileDialog ofd;
            ofd = new CustomOpenFileDialogForVista();
            ofd.FileEncoding = active.enc;
            ofd.InitialDirectory = this.lastOpenDirectory;
            ofd.Title = Resources.OpenFileDialogTitle;
            ofd.addFilter(Resources.FileDialogFilterShowOfAllFiles, Resources.FileDialogFilterExtOfAllFiles);
            if (ofd.ShowDialog() == CommonFileDialogResult.Ok)
            {
                active.LoadFileAsync(ofd.FileName, ofd.FileEncoding);
                this.lastOpenDirectory = Path.GetDirectoryName(ofd.FileName);
            }
            ofd.Dispose();
        }

        /// <summary>
        /// ファイルダイアログで指定したパスに保存する
        /// </summary>
        /// <param name="active">保存対象となるEditForm</param>
        public void SaveAs(EditForm active)
        {
            CustomFileDialog sfd;
            sfd = new CustomSaveFileDialogForVista();
            if (active.filepath != null)
                sfd.InitialDirectory = Path.GetDirectoryName(active.filepath);
            else
                sfd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            sfd.FileEncoding = active.enc;
            sfd.Title = Resources.OpenFileDialogTitle;
            sfd.addFilter(Resources.FileDialogFilterShowOfAllFiles, Resources.FileDialogFilterExtOfAllFiles);
            if (sfd.ShowDialog() == CommonFileDialogResult.Ok)
            {
                active.enc = sfd.FileEncoding;
                this.SaveFileAsync(active, sfd.FileName);
            }
            sfd.Dispose();
        }
        /// <summary>
        /// ファイルに保存する
        /// </summary>
        /// <param name="active">保存の対象となるEditForm</param>
        /// <param name="filename">ファイルパス</param>
        /// <remarks>
        /// エラーが発生した場合、ダイアログが表示されます。保存時に権限が足りない場合、管理者権限に昇格することがあります
        /// また、設定によってはバックアップが行われることもあります
        /// </remarks>
        public void SaveFileAsync(EditForm active, string filename)
        {
            bool backup = cfg.MaxBackupCount != 0;

            try
            {
                if (FileAccessPermissionHelper.IsGotPermission(Path.GetDirectoryName(filename)))
                    active.SaveFileAsync(filename, backup);
                else
                {
                    TaskDialog dialog = new TaskDialog();
                    dialog.Caption = Resources.UACSaveConfirmDialogTitle;
                    dialog.InstructionText = Resources.UACSaveConfirm;
                    dialog.Icon = TaskDialogStandardIcon.Shield;
                    dialog.StandardButtons = TaskDialogStandardButtons.Yes | TaskDialogStandardButtons.No;
                    if (dialog.Show() == TaskDialogResult.Yes)
                        active.SaveFileAsyncByAdministrator(filename, backup);
                    dialog.Dispose();
                }
            }
            catch (IOException ex)
            {
                throw new UserOperationException(ex.Message);
            }
        }

        /// <summary>
        /// ステートファイルからタブを復元する
        /// </summary>
        /// <param name="StateFilePath">対象となるステートファイルパス</param>
        public void LoadCurrentState(string StateFilePath)
        {
            if (File.Exists(StateFilePath) == false)
                return;
            BinaryFormatter formatter = new BinaryFormatter();
            FileStream fs = new FileStream(StateFilePath, FileMode.Open, FileAccess.Read);
            EditForm form = (EditForm)formatter.Deserialize(fs);
            fs.Close();

            this.CreateingEditFormEvent(this, new EditFormEventArgs(form));

            InitEditForm(form);
            form.DocumentType = form.DocumentType;
            form.linefeed = form.linefeed;
            form.enc = form.enc;

            form.Show(this.Panel, DockState.Document);
        }

        /// <summary>
        /// タブをステートファイルに保存する
        /// </summary>
        /// <param name="active">保存対象となるEditForm</param>
        /// <param name="stateFilePath">保存先のステートファイルパス</param>
        /// <param name="dataFilePath">実データーを収めるファイルパス</param>
        public void SaveCurrentState(EditForm active,string stateFilePath,string dataFilePath)
        {
            if (active == null)
                throw new ArgumentNullException();

            if (Directory.Exists(Config.ApplicationFolder) == false)
                Directory.CreateDirectory(Config.ApplicationFolder);

            BinaryFormatter formatter = new BinaryFormatter();
            FileStream fs = new FileStream(stateFilePath, FileMode.Create, FileAccess.Write);
            formatter.Serialize(fs, active);
            fs.Close();
        }

        void Panel_ActiveContentChanged(object sender, EventArgs e)
        {
            if(this.ActiveContentChanged != null)
                this.ActiveContentChanged(sender, e);
        }

        void InitEditForm(EditForm form)
        {
            form.fooTextBox1.TokenToColor = this.cfg.Token2Color;
            form.fooTextBox1.Font = new Font(this.cfg.FontName, this.cfg.FontSize);
            form.fooTextBox1.LineBreak = this.cfg.WordRap;
            form.fooTextBox1.DrawCaretLine = this.cfg.DrawLine;
            form.fooTextBox1.UrlMark = this.cfg.UrlMark;
            form.fooTextBox1.DrawLineNumber = this.cfg.DrawLineNumber;
            form.fooTextBox1.TabStops = this.cfg.TabStops;
            form.fooTextBox1.ShowFullSpace = true;
            form.fooTextBox1.ShowTab = true;
        }
    }

}
