using System;
using System.Linq;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using FooEditEngine;
using FooEditEngine.Windows;

namespace FooEditor
{
    public class ShowingCompleteBoxEventArgs : EventArgs
    {
        /// <summary>
        /// 入力された文字
        /// </summary>
        public char keyChar;
        /// <summary>
        /// 入力した単語と一致したコレクションのインデックス。一致しないなら-1をセットする
        /// </summary>
        public int foundIndex;
        /// <summary>
        /// 入力しようとした単語を設定する
        /// </summary>
        public string inputedWord;
        /// <summary>
        /// 補完対象のテキストボックス
        /// </summary>
        public FooTextBox textbox;
        public ShowingCompleteBoxEventArgs(char keyChar,FooTextBox textbox)
        {
            this.inputedWord = null;
            this.keyChar = keyChar;
            this.foundIndex = -1;
            this.textbox = textbox;
        }
    }

    public class SelectItemEventArgs : EventArgs
    {
        /// <summary>
        /// 補完候補
        /// </summary>
        public string word;
        /// <summary>
        /// 入力中の単語
        /// </summary>
        public string inputing_word;
        /// <summary>
        /// 補完対象のテキストボックス
        /// </summary>
        public FooTextBox textbox;
        public SelectItemEventArgs(string word, string inputing_word,FooTextBox textbox)
        {
            this.word = word;
            this.inputing_word = inputing_word;
            this.textbox = textbox;
        }
    }

    public delegate void SelectItemEventHandler(object sender,SelectItemEventArgs e);
    public delegate void ShowingCompleteBoxEnventHandler(object sender, ShowingCompleteBoxEventArgs e);

    public class AutocompleteBox
    {
        private ListBox listBox1 = new ListBox();
        private string inputedWord;

        public AutocompleteBox(FooTextBox frm)
        {
            this.listBox1.Visible = false;
            this.listBox1.Click += new EventHandler(listBox1_Click);
            frm.Controls.Add(this.listBox1);
            frm.KeyDown += new KeyEventHandler(frm_KeyDown);
            frm.KeyPress += new KeyPressEventHandler(frm_KeyPress);
            this.SelectItem = new SelectItemEventHandler((a, b) => { });
            this.ShowingCompleteBox = new ShowingCompleteBoxEnventHandler((s,e)=>{});
        }

        /// <summary>
        /// 補完すべき単語が選択されたときに発生するイベント
        /// </summary>
        public SelectItemEventHandler SelectItem;
        /// <summary>
        /// UI表示前のイベント
        /// </summary>
        public ShowingCompleteBoxEnventHandler ShowingCompleteBox;

        /// <summary>
        /// 区切り文字のリスト
        /// </summary>
        public char[] Operators
        {
            get;
            set;
        }

        /// <summary>
        /// オートコンプリートの対象となる単語のリスト
        /// </summary>
        public CompleteCollection<ICompleteItem> Items
        {
            get
            {
                return (CompleteCollection<ICompleteItem>)this.listBox1.DataSource;
            }
            set
            {
                this.listBox1.DataSource = value;
                this.listBox1.DisplayMember = CompleteCollection<ICompleteItem>.ShowMember;
            }
        }

        void frm_KeyPress(object sender, KeyPressEventArgs e)
        {
            FooTextBox textbox = (FooTextBox)sender;

            if (this.Operators == null || e.KeyChar == '\b' || e.KeyChar == '\r' || e.KeyChar == '\n' || this.ShowingCompleteBox == null)
                return;

            ShowingCompleteBoxEventArgs ev = new ShowingCompleteBoxEventArgs(e.KeyChar,textbox);
            ShowingCompleteBox(this, ev);

            if (ev.foundIndex != -1 && ev.inputedWord != null && ev.inputedWord != string.Empty)
            {
                this.inputedWord = ev.inputedWord;
                DecideListBoxLocation(textbox, this.listBox1);
                ResizeListBox(this.listBox1);
                this.listBox1.SelectedIndex = ev.foundIndex;
                this.listBox1.Visible = true;
            }
            else
                this.listBox1.Visible = false;
        }

        void DecideListBoxLocation(FooTextBox textbox,ListBox listbox)
        {
            TextPoint tp = textbox.CaretPostion;
            Point p = textbox.GetPostionFromTextPoint(tp);

            int height = (int)textbox.GetLineHeight(tp.row);

            if (p.Y + listbox.Size.Height + height > textbox.ClientSize.Height - SystemInformation.HorizontalScrollBarHeight)
                p.Y -= listbox.Size.Height;
            else
                p.Y += height;

            listbox.Location = p;
        }

        void ResizeListBox(ListBox listbox)
        {
            Graphics g = listbox.CreateGraphics();
            CompleteCollection<ICompleteItem> collection = this.Items;
            int width = (int)g.MeasureString(collection.LongestItem.word, this.listBox1.Font).Width;
            g.Dispose();

            listbox.Width = width + SystemInformation.VerticalScrollBarWidth;
        }

        void frm_KeyDown(object sender, KeyEventArgs e)
        {
            if (this.ShowingCompleteBox == null)
                return;

            if (this.listBox1.Visible == true && (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Tab))
            {
                this.listBox1_Click(this, new EventArgs());
                e.SuppressKeyPress = true;
            }
            
            switch (e.KeyCode)
            {
                case Keys.Escape:
                    this.listBox1.Visible = false;
                    break;
                case Keys.Down:
                    if (this.listBox1.Visible)
                    {
                        if (this.listBox1.SelectedIndex + 1 >= this.listBox1.Items.Count)
                            this.listBox1.SelectedIndex = this.listBox1.Items.Count - 1;
                        else
                            this.listBox1.SelectedIndex++;
                        e.Handled = true;
                    }
                    break;
                case Keys.Up:
                    if (this.listBox1.Visible)
                    {
                        if (this.listBox1.SelectedIndex - 1 < 0)
                            this.listBox1.SelectedIndex = 0;
                        else
                            this.listBox1.SelectedIndex--;
                        e.Handled = true;
                    }
                    break;
            }
        }

        private void listBox1_Click(object sender, EventArgs e)
        {
            CompleteWord selWord = (CompleteWord)this.listBox1.SelectedItem; 
            this.SelectItem(this,new SelectItemEventArgs(selWord.word, this.inputedWord,(FooTextBox)listBox1.Parent));
            this.listBox1.Visible = false;
            this.listBox1.Parent.Refresh();
        }

    }
}
