using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using nft.ui.command;
using System.Drawing;
using System.Diagnostics;
using nft.framework.drawing;
using SepalatorRequirement = nft.ui.command.CommandPathHelper.SepalatorRequirement;
using System.Reactive.Linq;

namespace nft.ui.mainframe {
    class MenuBarController : IMenuBarController {
        protected readonly MenuStrip mnRoot;
        protected CommandPathHelper<ToolStripMenuItem> pathHelper;

        public MenuBarController(MenuStrip mainmenu)
		{
            this.mnRoot = mainmenu;
            this.pathHelper = createPathHelper();
        }

        protected CommandPathHelper<ToolStripMenuItem> createPathHelper() {
            return new CommandPathHelper<ToolStripMenuItem>(this.GetItemIdOf);
        }

        protected string GetItemIdOf(ToolStripMenuItem item) {
            return item.Name;
        }

        #region menubar & menuitem functions
        public void AddMenuItem(CommandUI cui, CommandPathHint pathHint) {
            AddMenu(cui, pathHint);
        }

        public void BindCommand(ICommandProcedure procedure, CommandUI cmdUI, string path) {
            if (cmdUI == null || procedure == null) throw new ArgumentException("CommandUI or ICommandProcedure is null.");
            ToolStripItem m = FindOrCreate(path, cmdUI);
            ToolStripMenuItem item = m as ToolStripMenuItem;
            
            ITriggerCommandProcedure trigger = procedure as ITriggerCommandProcedure;
            if(trigger!=null) {
                Observable.FromEventPattern( x => item.Click += x, x => item.Click -= x
                ).Select(x => x.EventArgs).Subscribe(_ => trigger.Execute());
            }
            return;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="cui"></param>
        /// <param name="pathHint"></param>
        /// <returns></returns>
        public string AddMenu(CommandUI cui, CommandPathHint pathHint) {
            ToolStripMenuItem newitem = new ToolStripMenuItem(cui.LongCaption);
            newitem.Name = pathHint.ID;
            if (cui.Description != null) {
                newitem.ToolTipText = cui.Description;
            }
            if (cui.Icon != null) {
                // TODO: Icon
                /*
                cui.Icon.AddRef();
                newitem.Image = GetIconImage(cui);
                cui.Icon.ReleaseRef();
                 */
            }
            string mid = pathHint.ID;
            ToolStripMenuItem menuhost = FindMenu(pathHint.Parent);
            if (menuhost != null) {
                SepalatorRequirement spRequire;
                int n = pathHelper.FindInsertIndex(menuhost.DropDownItems, pathHint, out spRequire);
                InsertMenuItem(menuhost.DropDownItems, n, newitem, spRequire);
            } else {
                mnRoot.Items.Add(newitem);
            }
            //table.Add(newitem, cui);
            if (pathHint.Parent.Trim().Length > 0)
                return pathHint.Parent + CommandPathHelper.PathSepalator + mid;
            else
                return mid;
        }

        /// <summary>
        ///  find menuitem with the path string.
        /// </summary>
        /// <param name="path"></param>
        /// <returns>corresponding item or null if not found.</returns>
        public ToolStripMenuItem FindMenu(string path) {
            return FindOrCreateImpl(path, null);
        }

        /// <summary>
        /// find menuitem with the path string.
        /// create each node if not exist.
        /// </summary>
        /// <param name="path"></param>
        /// <param name="ui"></param>
        /// <returns></returns>
        public ToolStripMenuItem FindOrCreate(string path, CommandUI ui) {
            return FindOrCreateImpl(path, ui);
        }

        protected ToolStripMenuItem FindOrCreateImpl(string path, CommandUI ui) {
            ToolStripMenuItem p = null;
            if (!path.Equals("")) {
                string[] elms = CommandPathHelper.SplitIntoNode(path);
                ToolStripItemCollection children = mnRoot.Items;
                string mid;
                int s = elms[0].Length > 0 ? 0 : 1;
                for (int i = s; i < elms.Length; i++) {
                    mid = elms[i];
                    p = FindMenu(children, mid);
                    if (p == null) {
                        if (ui!=null) {
                            p = new ToolStripMenuItem(ui.LongCaption);
                            SepalatorRequirement spRequire;
                            int n = pathHelper.FindInsertIndex(children, mid, out spRequire);
                            InsertMenuItem(children, n, p, spRequire);
                        } else {
                            break;
                        }
                    }
                    children = p.DropDownItems;
                }
            }
            return p;
        }

        

        /// <summary>
        /// find menuitem witch has specifed ID within the item collections.
        /// </summary>
        /// <param name="children"></param>
        /// <param name="mid"></param>
        /// <returns></returns>
        protected ToolStripMenuItem FindMenu(ToolStripItemCollection children, string mid) {
            ToolStripItem lastitem = null;
            foreach (ToolStripItem item in children) {
                if (!(item is ToolStripMenuItem)) continue;
                lastitem = item;
                if (item.Name.Equals(mid))
                    return item as ToolStripMenuItem;
            }
            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        public ToolStripMenuItem FindOrCreate(ToolStripItemCollection children, string mid, string newcap) {
            throw new NotImplementedException();
        }
        #endregion

        protected void InsertMenuItem(ToolStripItemCollection children, int index, ToolStripMenuItem neIitem, SepalatorRequirement spRequire) {
            try {
                children.Insert(index, neIitem);
            } catch (ArgumentOutOfRangeException ex) {
                Debug.WriteLine(ex);
            }
            try {
                switch (spRequire) {
                    case SepalatorRequirement.BEFORE:
                        children.Insert(index, NewSepalator());
                        break;
                    case SepalatorRequirement.AFTER:
                        children.Insert(index+1, NewSepalator());
                        break;
                }            
            } catch (ArgumentOutOfRangeException ex2) {
                Debug.WriteLine(ex2);
            }
        }

 
		protected ToolStripItem NewSepalator()
		{
			ToolStripItem item = new ToolStripSeparator();
			return item;
		}


    }
}
