using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using ChaKi.Common;
using ChaKi.Common.Settings;
using ChaKi.Entity.Corpora;
using ChaKi.GUICommon;
using ChaKi.Service.DependencyEdit;
using DependencyEditSLA.Widgets;
using MessageBox = ChaKi.Common.Widgets.MessageBox;
using System.Text;
using ChaKi.Service.Cabocha;
using System.IO;
using ChaKi.Service.Export;
using ChaKi.Service.Readers;

namespace DependencyEditSLA
{
    public partial class DepEditControl : UserControl
    {
        private SentenceStructureScrollPanel m_ContainerPanel;
        private SentenceStructure m_SentenceStructure;
        private Panel m_DummyPanel;
        private LexemeSelectionGrid m_LexemeSelector;

        private int m_SentenceID;
        private int m_CenterWordPos;
        private IDepEditService m_Service;
        static private DepEditControl m_instance;
        private Corpus m_Corpus;
        private int m_CenterWordStartAt;

        /// <summary>
        /// 語の上にマウスを持ってきたときにGuide Panelの表示更新を通知するイベント
        /// </summary>
        public event UpdateGuidePanelDelegate UpdateGuidePanel;

        /// <summary>
        /// SegmentBoxの上にマウスを持ってきたときにAttribute Panelの表示更新を通知するイベント
        /// TODO: Link, Groupに対してもも追加する必要がある.
        /// </summary>
        public event UpdateAttributePanelDelegate UpdateAttributePanel;

        /// <summary>
        /// Goto Previous/Next Sentenceが指示されたことを通知するイベント
        /// </summary>
        public event ChangeSentenceDelegate ChangeSenenceRequested;


        public DepEditControl()
        {
            m_ContainerPanel = new SentenceStructureScrollPanel();
            m_SentenceStructure = new DependencyEditSLA.SentenceStructure();
            m_DummyPanel = new Panel();

            InitializeComponent();

            m_SentenceStructure.BackColor = ColorTranslator.FromHtml(DepEditSettings.Current.Background);
            m_SentenceStructure.Location = new Point(0, 0);
            m_SentenceStructure.Margin = new Padding(3, 116, 3, 116);
            m_SentenceStructure.Name = "m_SentenceStructure";
            m_SentenceStructure.Size = new Size(732, 184);
            m_SentenceStructure.TabIndex = 1;
            m_DummyPanel.BackColor = Color.Linen;
            m_DummyPanel.Location = new Point(0, 0);
            m_DummyPanel.Size = m_SentenceStructure.Size;

            this.toolStripContainer2.ContentPanel.Controls.Add(m_ContainerPanel);
            m_ContainerPanel.Dock = DockStyle.Fill;
            m_ContainerPanel.Controls.Add(m_SentenceStructure);
            m_ContainerPanel.Controls.Add(m_DummyPanel);    // SentenceStrucureの再構成中のみ見せるパネルで、Containerのサイズを維持するためのもの.
            m_ContainerPanel.AutoScroll = true;
            m_SentenceStructure.OnLayoutChanged += new EventHandler(m_SentenceStructure_OnLayoutChanged);
            m_SentenceStructure.ScrollResetRequested += new EventHandler(m_SentenceStructure_ScrollResetRequested);
            
            m_SentenceStructure.DispMode = DepEditSettings.Current.DispMode;
            m_SentenceStructure.UpdateGuidePanel += new UpdateGuidePanelDelegate(m_SentenceStructure_UpdateGuidePanel);
            m_SentenceStructure.UpdateAttributePanel += new UpdateAttributePanelDelegate(m_SentenceStructure_UpdateAttributePanel);
            m_SentenceStructure.OnSentenceNoChanged += new EventHandler(m_SentenceStructure_OnSentenceNoChanged);
            FontDictionary.Current.FontChanged += new EventHandler(FontChangedHandler);
            m_CenterWordPos = -1;
            m_Service = new DepEditService();
            m_instance = this;
            m_Corpus = null;
            m_IsEditMode = false;
        }

        void FontChangedHandler(object sender, EventArgs e)
        {
            m_SentenceStructure.BackColor = ColorTranslator.FromHtml(DepEditSettings.Current.Background);
            m_SentenceStructure.Refresh();
        }

        static public DepEditControl Instance
        {
            get { return m_instance; }
        }

        public Corpus Cps
        {
            get { return m_Corpus; }
        }

        private bool m_IsEditMode;
        public bool IsEditMode
        {
            get { return m_IsEditMode; }
            set
            {
                if (value)
                {
                    // ViewMode -> EditMode
                    BeginSentenceEdit(m_Corpus, m_SentenceID, m_CenterWordPos);
                }
                else
                {
                    // EditMode -> ViewMode
                    EndEditing();
                }
                m_IsEditMode = value;
            }
        }

        /// <summary>
        /// Dependency構造の編集を開始する
        /// </summary>
        /// <param name="cps">コーパス</param>
        /// <param name="sid">文番号</param>
        /// <param name="cid">中心語の位置（強調表示に使用. 無指定[-1]でも可）</param>
        public bool BeginSentenceEdit(Corpus cps, int sid, int cid)
        {
            // 現在の状態が編集中なら、まずその編集を終わらせるかどうかを確認する。
            EndEditing();

            m_SentenceID = sid;
            m_CenterWordPos = cid;
            m_Corpus = cps;
            if (!m_Corpus.Mutex.WaitOne(10, false))
            {
                MessageBox.Show("DepEditControl: Could not lock the Corpus.");
                return true;
            }
            try
            {
                // コーパスよりSentenceを取得する
                Sentence sen = m_Service.Open(m_Corpus, sid, HandleUnlockRequest);
                if (sen == null)
                {
                    MessageBox.Show("DepEditControl: No more sentence found, or sentence ID is invalid.");
                    Terminate();
                    return false;
                }
                // cidをDepEditServiceに伝える。
                if (cid >= 0 && cid < sen.Words.Count)
                {
                    m_CenterWordStartAt = ((Word)sen.Words[cid]).StartChar;
                }
                else
                {
                    m_CenterWordStartAt = sen.StartChar;
                }
                m_Service.CenterWordStartAt = m_CenterWordStartAt;
                // Projectを決める
                IList<Project> projs = m_Service.RetrieveProjects();
                if (projs.Count == 1)
                {
                    m_Service.SetupProject(projs[0].ID);   // 1つしかProjectを持っていなければ既定でそれを用いる.
                }
                else if (projs.Count > 1)
                {
                    return true; //TODO: Projectが複数ある場合は、ユーザが選択するようにする。
                }
                else
                {
                    MessageBox.Show("DepEditControl: This corpus has no Project definition.");
                    Terminate();
                    return true;
                }
                // 辞書設定をServiceに通知
                foreach (DictionarySettingItem item in DictionarySettings.Instance)
                {
                    Dictionary dict = Dictionary.CreateFromFile(item.Path);
                    m_Service.AddReferenceDictionary(dict);
                }

                m_LexemeSelector = new LexemeSelectionGrid(m_Corpus, m_Service);

                m_SentenceStructure.SetSentence(sen, m_Service, m_LexemeSelector);
                m_SentenceStructure.SetCenterWord(m_CenterWordPos);
                m_SentenceStructure.VerticalScroll.Value = m_SentenceStructure.VerticalScroll.Maximum;
                m_SentenceStructure.UpdateContents();

                m_IsEditMode = true;

            }
            catch (Exception ex)
            {
                ErrorReportDialog dlg = new ErrorReportDialog("Starting Dependency Edit", ex);
                dlg.ShowDialog();
                Terminate();
            }
            finally
            {
                if (m_Corpus != null)
                {
                    m_Corpus.Mutex.ReleaseMutex();
                }
            }
            return true;
        }

        /// <summary>
        /// 編集を終了する。セーブする必要がある場合はユーザーに問い合わせてCommitする。
        /// </summary>
        /// <returns>Cancelする場合はfalseを返す</returns>
        public bool EndEditing()
        {
            try
            {
                if (m_Service.CanSave())
                {
                    DialogResult res = MessageBox.Show("Save Current Changes?", "DependencyEdit", MessageBoxButtons.YesNoCancel);
                    if (res == DialogResult.Yes)
                    {
                        if (!Save())
                        {
                            return false;
                        }
                        m_Service.CannotSave(); // CanSave状態をfalseにする
                    }
                    else if (res == DialogResult.No)
                    {
                        m_Service.CannotSave(); // CanSave状態をfalseにする
                    }
                    else if (res == DialogResult.Cancel)
                    {
                        return false;
                    }
                }
                m_Service.Close();
                if (m_LexemeSelector != null) m_LexemeSelector.Dispose();
                m_IsEditMode = false;
            }
            catch (Exception ex)
            {
                ErrorReportDialog dlg = new ErrorReportDialog("Cannot Execute operation", ex);
                dlg.ShowDialog();
            }
            return true;
        }

        public void Terminate()
        {
            m_Corpus = null;
            m_SentenceStructure.SetSentence(null, m_Service, m_LexemeSelector);
            m_SentenceStructure.UpdateContents();
        }

        public bool HandleUnlockRequest(Type requestingService)
        {
            if (requestingService == typeof(IDepEditService))
            {
                // 自分自身からのUnlockリクエストは無視する.
                return true;
            }
            if (!m_Service.CanSave())
            {
                EndEditing();
                return true;
            }
            return false;
        }

        private bool Save()
        {
            if (!CheckForUnassignedLexeme())
            {
                MessageBox.Show("Cannot save: Incomplete word exists.", "DependencyEdit", MessageBoxButtons.OK);
                return false;
            }
            m_Service.Commit();

            return true;
        }

        
        /// <summary>
        /// POSが"Unassigned"になっているWordが存在するかチェックする.
        /// 存在すればfalseを返す.
        /// </summary>
        /// <returns></returns>
        private bool CheckForUnassignedLexeme()
        {
            foreach (Word w in m_SentenceStructure.Model.Words)
            {
                if (w.Lex.PartOfSpeech == PartOfSpeech.Default)
                {
                    return false;
                }
            }
            return true;
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            m_SentenceStructure.Undo();
        }

        private void toolStripButton2_Click(object sender, EventArgs e)
        {
            m_SentenceStructure.Redo();
        }

        /// <summary>
        /// アイドル時の処理として、UI状態を更新する。
        /// Programから呼び出す必要がある。
        /// </summary>
        public static void UIUpdate()
        {
            if (m_instance == null || m_instance.m_SentenceStructure == null) return;
            m_instance.toolStripButton1.Enabled = (m_instance.m_IsEditMode && m_instance.m_SentenceStructure.CanUndo());
            m_instance.toolStripButton2.Enabled = (m_instance.m_IsEditMode && m_instance.m_SentenceStructure.CanRedo());
            m_instance.toolStripButton8.Enabled = (m_instance.m_IsEditMode && m_instance.m_SentenceStructure.CanSave());
            m_instance.toolStripButton9.Enabled = (m_instance.ChangeSenenceRequested != null);
            m_instance.toolStripButton10.Enabled = (m_instance.ChangeSenenceRequested != null);
            m_instance.toolStripButton6.Checked = (m_instance.m_Corpus != null && m_instance.m_IsEditMode);
            m_instance.toolStripButton6.Enabled = (m_instance.m_Corpus != null);
            m_instance.toolStripButton14.Enabled = (m_instance.m_IsEditMode && m_instance.m_Corpus != null && m_instance.m_SentenceStructure.Model != null);
        }

        /// <summary>
        /// Saveボタンが押されたときの処理。
        /// 編集内容をコミットし、同じ文の編集を続ける。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripButton8_Click(object sender, EventArgs e)
        {
            try
            {
                if (!Save())
                {
                    return;
                }
                Sentence sen = m_Service.ReOpen();
                m_SentenceStructure.SetSentence(sen, m_Service, m_LexemeSelector);
                m_SentenceStructure.SetCenterWord(m_CenterWordPos);
                m_SentenceStructure.UpdateContents();
                // 辞書を再追加
                foreach (DictionarySettingItem item in DictionarySettings.Instance)
                {
                    Dictionary dict = Dictionary.CreateFromFile(item.Path);
                    m_Service.AddReferenceDictionary(dict);
                }
                // cidをDepEditServiceに再設定
                m_Service.CenterWordStartAt = m_CenterWordStartAt;
            }
            catch (Exception ex)
            {
                ErrorReportDialog dlg = new ErrorReportDialog("Cannot Execute operation", ex);
                dlg.ShowDialog();
            }
        }

        /// <summary>
        /// EditModeボタンが押されたときの処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripButton6_Click(object sender, EventArgs e)
        {
            this.IsEditMode = !this.IsEditMode;
        }

        /// <summary>
        /// GraphVizのDOTファイルへエクスポートする。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripButton12_Click(object sender, EventArgs e)
        {
            FileDialog fd = new SaveFileDialog();
            fd.Filter = "GraphViz DOT files (*.dot)|*.dot|All files (*.*)|*.* ";
            if (fd.ShowDialog() != DialogResult.OK)
            {
                return;
            }

            WriteToDotFile(fd.FileName);
        }

        public void WriteToDotFile(string filename)
        {
            m_SentenceStructure.WriteToDotFile(filename);
        }

        // "View Diagonal"
        private void verticalToolStripMenuItem_Click(object sender, EventArgs e)
        {
            m_SentenceStructure.DispMode = DispModes.Diagonal;
            DepEditSettings.Current.DispMode = m_SentenceStructure.DispMode;
        }

        // "View Horizontal"
        private void horizonalToolStripMenuItem_Click(object sender, EventArgs e)
        {
            m_SentenceStructure.DispMode = DispModes.Horizontal;
            DepEditSettings.Current.DispMode = m_SentenceStructure.DispMode;
        }

        // View "Morphemes"
        private void morphemesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            m_SentenceStructure.DispMode = DispModes.Morphemes;
            DepEditSettings.Current.DispMode = m_SentenceStructure.DispMode;
        }

        // "Zoom"
        void toolStripButton3_Click(object sender, System.EventArgs e)
        {
            m_SentenceStructure.Zoom(); 
        }

        // "Pan"
        void toolStripButton4_Click(object sender, System.EventArgs e)
        {
            m_SentenceStructure.Pan();
        }

        // "1:1"
        void toolStripButton5_Click(object sender, System.EventArgs e)
        {
            m_SentenceStructure.ZoomToDefault();
        }

        void m_SentenceStructure_UpdateGuidePanel(Corpus corpus, Lexeme lex)
        {
            if (this.UpdateGuidePanel != null)
            {
                UpdateGuidePanel(corpus, lex);
            }
        }

        void m_SentenceStructure_UpdateAttributePanel(Corpus corpus, object source)
        {
            if (this.UpdateAttributePanel != null)
            {
                UpdateAttributePanel(corpus, source);
            }
        }

        void m_SentenceStructure_OnLayoutChanged(object sender, EventArgs e)
        {
            m_DummyPanel.Width = m_SentenceStructure.Width;
            m_DummyPanel.Height = m_SentenceStructure.Height;
        }

        void m_SentenceStructure_ScrollResetRequested(object sender, EventArgs e)
        {
            m_ContainerPanel.HorizontalScroll.Value = 0;
            m_ContainerPanel.VerticalScroll.Value = 0;
        }

        void m_SentenceStructure_OnSentenceNoChanged(object sender, EventArgs e)
        {
            if (m_Corpus != null && m_SentenceStructure.Model != null)
            {
                this.toolStripTextBox1.Text = m_SentenceStructure.Model.ID.ToString();
                this.toolStripTextBox2.Text = m_Corpus.Name;
            }
            else
            {
                this.toolStripTextBox1.Text = string.Empty;
                this.toolStripTextBox2.Text = string.Empty;
            }
        }

        // Goto Previous Sentenceの処理
        private void toolStripButton9_Click(object sender, EventArgs e)
        {
            if (m_Corpus == null || m_SentenceStructure.Model == null) return;
            if (this.ChangeSenenceRequested != null)
            {
                ChangeSenenceRequested(m_Corpus, m_SentenceStructure.Model.ID, -1, false);
            }
        }

        // Goto Next Sentenceの処理
        private void toolStripButton10_Click(object sender, EventArgs e)
        {
            if (m_Corpus == null || m_SentenceStructure.Model == null) return;
            if (this.ChangeSenenceRequested != null)
            {
                ChangeSenenceRequested(m_Corpus, m_SentenceStructure.Model.ID, 1, false);
            }
        }

        // Goto Previous Sentence in KWIC Listの処理
        private void toolStripButton11_Click(object sender, EventArgs e)
        {
            if (m_Corpus == null || m_SentenceStructure.Model == null) return;
            if (this.ChangeSenenceRequested != null)
            {
                ChangeSenenceRequested(m_Corpus, m_SentenceStructure.Model.ID, -1, true);
            }
        }

        // Goto Next Sentence in KWIC Listの処理
        private void toolStripButton13_Click(object sender, EventArgs e)
        {
            if (m_Corpus == null || m_SentenceStructure.Model == null) return;
            if (this.ChangeSenenceRequested != null)
            {
                ChangeSenenceRequested(m_Corpus, m_SentenceStructure.Model.ID, 1, true);
            }
        }

        // Goto Line Number（EditBoxでMouseDown時）の処理
        private void toolStripTextBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (m_Corpus == null || m_SentenceStructure.Model == null) return;
            using (LineNumberInputForm dlg = new LineNumberInputForm())
            {
                Point loc = this.toolStrip1.PointToScreen(e.Location);
                loc.Y += 50;
                dlg.Location = loc;
                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    int ln = dlg.LineNumber;
                    if (ln >= 0 && this.ChangeSenenceRequested != null)
                    {
                        ChangeSenenceRequested(m_Corpus, -1, ln, false);
                    }
                }
            }
        }

        // Run cabocha to reanalyze the text
        private void toolStripButton14_Click(object sender, EventArgs e)
        {
            if (m_Corpus == null || m_SentenceStructure.Model == null) return;

            try
            {
                var sen = m_SentenceStructure.Model;

                var cabocha = new CabochaRunner();

                var segs = m_Service.GetNestTags();
                string output = cabocha.ParseWithSegmentInfo(m_Corpus, sen, segs);

                m_Service.UpdateAllBunsetsu(sen, output);
                m_SentenceStructure.UpdateContents();
            }
            catch (Exception ex)
            {
                var edlg = new ErrorReportDialog("Command failed.", ex);
                edlg.ShowDialog();
            }
        }
    }
}
