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 ToolBarController : IToolBarController {
        static public readonly Color DefaultTranspalentColor = Color.Magenta;
        protected IDictionary<ImageRef, ImageList> imglistTable = new Dictionary<ImageRef, ImageList>();
        protected readonly ToolStripPanel stripPanel;
        protected CommandPathHelper<ToolStripMenuItem> pathHelper;

        public ToolBarController(ToolStripPanel bar)
		{
			this.stripPanel = bar;
            this.pathHelper = createPathHelper();
        }

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

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

        #region toolbar & tool button functions
        public void AddToolButton(CommandUI cui, CommandPathHint pathHint) {
            AddButton(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 item = FindOrCreateImpl(path, cmdUI);
            
            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>
        /// Add new tool button
        /// </summary>
        /// <param name="cui"></param>
        /// <param name="pathHint"></param>
        /// <returns></returns>
        public ToolStripItem AddButton(CommandUI cui, CommandPathHint pathHint) {
            ToolStrip bar = FindBar(pathHint.Parent, true);            
            // create and add to tool bar.
            ToolStripItem newitem = CreateToolStripItem(cui, bar);
            ToolStripItemCollection children = bar.Items;

            newitem.Name = pathHint.ID;
            SepalatorRequirement spRequire;
            int n = pathHelper.FindInsertIndex(children, pathHint.ID, out spRequire);
            InsertToolItem(children, n, newitem, spRequire);
            bar.Items.Insert(n, newitem);
            return newitem;
        }

        /// <summary>
        /// find toolbar by name.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ToolStrip FindBar(string name, bool createIfNotExist) {
            ToolStrip target = stripPanel.Controls[name] as ToolStrip;

            if (createIfNotExist && target == null) {
                target = CreateNewToolBar(name, DefaultTranspalentColor, new Size(16, 16));
            }
            return target;
        }

        /// <summary>
        /// create new toolbar
        /// </summary>
        /// <param name="barname"></param>
        /// <param name="transpalent"></param>
        /// <param name="buttonsize"></param>
        /// <returns></returns>
        public ToolStrip CreateNewToolBar(string barname, Color transpalent, Size buttonsize) {
            ToolStrip bar = new ToolStrip();
            stripPanel.Controls.Add(bar);
            bar.Name = barname;
            bar.ImageList = new ImageList();
            bar.ImageList.TransparentColor = transpalent;
            bar.ImageList.ImageSize = buttonsize;
            bar.Tag = new ImageListIndexResolver(bar.ImageList);
            bar.Disposed += new EventHandler(bar_Disposed);
            return bar;
        }
        #endregion

        protected ToolStripItem FindOrCreateImpl(string path, CommandUI ui) {
            ToolStripItem p = null;
            if (!path.Equals("")) {
                string[] elms = CommandPathHelper.SplitIntoNode(path);
                string parent, selfId;
                int length = elms.Length;
                if (length > 1) {
                    selfId = elms[--length];
                    parent = elms[--length];
                } else {
                    parent = "default";
                    selfId = elms[--length];
                }

                ToolStrip bar = FindBar(parent, true);
                ToolStripItemCollection children = bar.Items;
                p = FindItem(children, selfId);
                if (p == null) {
                    p = CreateToolStripItem(ui, bar);
                    SepalatorRequirement spRequire;
                    int n = pathHelper.FindInsertIndex(children, selfId, out spRequire);
                    InsertToolItem(children, n, p, spRequire);
                }
            }
            return p;
        }

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

        protected void InsertToolItem(ToolStripItemCollection children, int index, ToolStripItem 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);
            }
        }


        /// <summary>
        /// create tool button.
        /// </summary>
        /// <param name="info"></param>
        /// <param name="bar"></param>
        /// <returns></returns>
        protected virtual ToolStripButton CreateToolStripItem(CommandUI cui, ToolStrip bar) {
            ToolStripButton newitem = new ToolStripButton();
            newitem.ToolTipText = cui.Description;
            if (cui.Description != null) {
                newitem.ToolTipText = cui.Description;
            }
            ImageRef iref = cui.Icon;
            if (iref != null) {
                iref.AddRef();
                if (iref.TranspalentColor != ImageRef.Undefined) {
                    newitem.ImageTransparentColor = iref.TranspalentColor;
                }
                newitem.Image = GetIconImage(cui);
                iref.ReleaseRef();
            }
            return newitem;
        }

        protected Image GetIconImage(CommandUI cui) {
            int idx = cui.IconIndex;
            ImageRef iref = cui.Icon;
            if (idx == CommandUI.IconIndexUnassinged) {
                return iref.Image;
            } else {
                ImageList il;
                if (!imglistTable.TryGetValue(iref, out il)) {
                    il = new ImageList();
                    il.Images.AddStrip(iref.Image);
                    if (iref.TranspalentColor != ImageRef.Undefined) {
                        il.TransparentColor = iref.TranspalentColor;
                    }
                    il.ImageSize = EstimateIconSize(iref.Image);
                    imglistTable.Add(iref, il);
                }
                return il.Images[idx];
            }
        }

        protected Size EstimateIconSize(Image listimg) {
            Size sz = listimg.Size;
            int n = sz.Width / sz.Height;
            int m = sz.Width % sz.Height;
            // Assume square icons at first
            sz.Width = sz.Height;
            if (m != 0) {
                //divide reminder of total width.
                if (m * 2 > sz.Height) {
                    //Icon width seems to be shorter than height.
                    m = sz.Height - m;
                    sz.Width -= m / n;
                } else {
                    //Icon width seems to be longer than height.
                    sz.Width += m / n;
                }
            }
            return sz;
        }

        /// <summary>
        /// dispose event handler for toolbars
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void bar_Disposed(object sender, EventArgs e) {
            ToolStrip bar = (ToolStrip)sender;
            ImageListIndexResolver resolver = bar.Tag as ImageListIndexResolver;
            if (resolver != null) {
                bar.ImageList.Dispose();
                bar.Tag = null;
            }
        }
 
		protected ToolStripItem NewSepalator()
		{
			ToolStripItem item = new ToolStripSeparator();
			return item;
		}

    }
}
