using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Xml;
using System.Xml.Schema;
using System.ComponentModel.Composition;
using FooEditEngine;
using FooEditEngine.Windows;
using FooEditor;
using FooEditor.Plugin;
using XmlCompleter.Properties;
using System.Windows.Forms;
using Microsoft.WindowsAPICodePack.Dialogs;

namespace XmlCompleter
{
    [Export(typeof(IPlugin))]
    public class Main : IPlugin
    {
        Editor editor;
        bool inputedTag = false;

        public void Initalize(Editor e)
        {
            editor = e;
            editor.CreatedEditFromEvent += new CreateEditFormEventHandler(editor_CratedEditFromEvent);

            ToolStripMenuItem item = (ToolStripMenuItem)e.RootMenuStrip.Items[3];
            item.DropDownItems.Insert(0, new ToolStripMenuItem("XSDから補完候補を作成する", null, CreateCompleteItemFromXSD));
        }

        private void CreateCompleteItemFromXSD(object sender, EventArgs e)
        {
            EditForm active = editor.EditForm;

            if (active == null)
                return;

            CustomFileDialog ofd;
            ofd = new CustomOpenFileDialogForVista();
            ofd.addFilter(Resources.XSDFileFilter, "*.xsd");

            CommonFileDialogResult result = ofd.ShowDialog();
            string filepath = ofd.FileName;

            ofd.Dispose();

            if (result == CommonFileDialogResult.Cancel)
                return;

            AutocompleteBox box = active.CompleteBox;

            XmlTextReader reader = new XmlTextReader(filepath);
            XmlSchema schema = XmlSchema.Read(reader, null);
            if (box != null)
            {
                foreach (XmlSchemaElement element in schema.Items)
                {
                    box.Items.Add(new XmlCompleteItem(element.Name, false));
                    XmlSchemaComplexType type = element.SchemaType as XmlSchemaComplexType;
                    if (type == null)
                        continue;
                    foreach (XmlSchemaAttribute attr in type.Attributes)
                        box.Items.Add(new XmlCompleteItem(attr.Name, true));
                }
            }
            reader.Close();
        }

        void editor_CratedEditFromEvent(object sender, EditFormEventArgs e)
        {
            e.edit.DocumentChangeTypeEvent += new FooEditor.DocumentTypeChangeEventHandler(edit_DocumentChangeTypeEvent);
        }

        void edit_DocumentChangeTypeEvent(object sender, EventArgs e)
        {
            EditForm editForm = (EditForm)sender;

            if (editForm.DocumentType == null)
                return;

            if (editForm.SynataxDefnition != null)
            {
                if (editForm.CompleteBox == null || editForm.SynataxDefnition.Hilighter == "xml")
                {
                    AutocompleteBox box = new AutocompleteBox(editForm.fooTextBox1);
                    box.ShowingCompleteBox = new ShowingCompleteBoxEnventHandler(OnPreShow);
                    box.SelectItem = new SelectItemEventHandler(OnDoAutocomplete);
                    box.Items = new CompleteCollection<ICompleteItem>();
                    editForm.CompleteBox = box;
                }

                AutocompleteBox CompleteBox = editForm.CompleteBox;
                CompleteBox.Items.Clear();

                editForm.CompleteBox.Operators = editForm.SynataxDefnition.Operators;

                foreach (string word in editForm.SynataxDefnition.Keywords)
                    CompleteBox.Items.Add(new XmlCompleteItem(word, false));
                foreach (string word in editForm.SynataxDefnition.Keywords2)
                    CompleteBox.Items.Add(new XmlCompleteItem(word, true));
            }
        }

        private void OnDoAutocomplete(object sender, SelectItemEventArgs e)
        {
            FooTextBox textbox = e.textbox;
            string inputing_word = e.inputing_word;
            string word = e.word;

            //キャレットは入力された文字の後ろにあるので、こうする
            int start = textbox.SelectionStart - inputing_word.Length;
            if (start < 0)
                start = 0;

            textbox.Document.Replace(start, inputing_word.Length, word);

            textbox.Refresh();
        }

        private void OnPreShow(object sender, ShowingCompleteBoxEventArgs e)
        {
            AutocompleteBox box = (AutocompleteBox)sender;

            int inputingIndex = e.textbox.SelectionStart - 1;
            if (inputingIndex < 0)
                inputingIndex = 0;

            this.inputedTag = IsInputedTag(e.textbox.Document, inputingIndex);

            e.inputedWord = GetWord(e.textbox.Document, inputingIndex, box.Operators) + e.keyChar;

            for (int i = 0; i < box.Items.Count; i++)
            {
                XmlCompleteItem item = (XmlCompleteItem)box.Items[i];
                if (item.word.StartsWith(e.inputedWord))
                {
                    if (inputedTag && !item.Attribute)
                        continue;
                    e.foundIndex = i;
                    break;
                }
            }
        }

        string GetWord(Document doc, int startIndex, char[] sep)
        {
            if (doc.Length == 0)
                return null;

            StringBuilder word = new StringBuilder();
            for (int i = startIndex; i >= 0; i--)
            {
                if (sep.Contains(doc[i]))
                {
                    return word.ToString();
                }
                word.Insert(0, doc[i]);
            }
            if (word.Length > 0)
                return word.ToString();
            else
                return null;
        }

        bool IsInputedTag(Document doc, int index)
        {
            if (doc.Length == 0)
                return false;

            bool hasSpace = false;

            for (int i = index; i >= 0; i--)
            {
                if (doc[i] == ' ')
                {
                    hasSpace = true;
                }
                else if (doc[i] == '<')
                {
                    if (hasSpace)
                        return true;
                    else
                        break;
                }
            }
            return false;
        }

        public void ClosedApp()
        {
            return;
        }

        public void ShowConfigForm()
        {
            return;
        }
    }
}
