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;

namespace nft.ui.mainframe {
    class ToolStripManager {
        static public readonly Color DefaultTranspalentColor = Color.Magenta;
        protected readonly MenuStrip mnRoot;
        //protected readonly Dictionary<ToolStripItem, MenuCreationInfo> menuitems;
        protected readonly ToolStripPanel stripPanel;
        protected IDictionary<ImageRef, ImageList> imglistTable;

        public ToolStripManager(ToolStripPanel bar, MenuStrip mainmenu)
		{
			this.stripPanel = bar;
            this.mnRoot = mainmenu;
            imglistTable = new Dictionary<ImageRef, ImageList>();
            //menuitems = new Dictionary<ToolStripItem, MenuCreationInfo>();
        }

        #region menubar & menuitem functions
        public bool SetCommand(string cmdID, ICommandEntity_Old entity, string pathname) {
            ToolStripItem m = FindOrCreate(pathname, cmdID);
            ToolStripMenuItem item = m as ToolStripMenuItem;
            if (item == null || entity == null) return false;

            CommandUI_Old cmdUI = CommandUI_Old.GetCommandUI(cmdID);
            if (cmdUI == null)
                cmdUI = new CommandUI_Old(cmdID, entity);
            cmdUI.AddTrigger(CommandTriggerFactory.Create(item));
            return true;
        }

        /// <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) {
                cui.Icon.AddRef();
                newitem.Image = GetIconImage(cui);
            }
            string mid = pathHint.ID;
            ToolStripMenuItem menuhost = FindMenu(pathHint.Parent);
            if (menuhost != null) {
                int n = FindInsertIndex(menuhost.DropDownItems, pathHint);
                try {
                    menuhost.DropDownItems.Insert(n, newitem);
                } catch (ArgumentOutOfRangeException ex) {
                    Debug.WriteLine(ex);
                }
            } else {
                mnRoot.Items.Add(newitem);
            }
            //table.Add(newitem, cui);
            if (pathHint.Parent.Trim().Length > 0)
                return pathHint.Parent + CommandManager.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="newcap"></param>
        /// <returns></returns>
        public ToolStripMenuItem FindOrCreate(string path, string newcap) {
            return FindOrCreateImpl(path, newcap);
        }

        protected ToolStripMenuItem FindOrCreateImpl(string path, string newcap) {
            ToolStripMenuItem p = null;
            if (!path.Equals("")) {
                string[] elms = path.Split(new char[] { CommandManager.PathSepalator });
                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 (newcap!=null) {
                            p = new ToolStripMenuItem(newcap);
                            int n = FindInsertIndex(children, mid);
                            children.Insert(n, p);
                        } 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) {
            ToolStripItem lastitem = null;
            foreach (ToolStripItem item in children) {
                if (!(item is ToolStripMenuItem)) continue;
                lastitem = item;
                if (item.Name.Equals(mid))
                    return item as ToolStripMenuItem;
            }
            ToolStripMenuItem newone = new ToolStripMenuItem(newcap);
            int n = FindInsertIndex(children, mid);
            children.Insert(n, newone);
            return newone as ToolStripMenuItem;
        }
        #endregion

        #region toolbar & button functions
        public bool SetCommand(string cmdID, ICommandEntity_Old entity, string barname, string bid) {
            if (entity == null) return false;
            ToolStrip bar = FindBar(barname);
            if (bar == null) return false;
            foreach (ToolStripItem item in bar.Items) {
                if (!(item is ToolStripButton) || item.Name == null) continue;
                if (item.Name.Equals(bid)) {
                    CommandUI_Old cmdUI = CommandUI_Old.GetCommandUI(cmdID);
                    if (cmdUI == null)
                        cmdUI = new CommandUI_Old(cmdID, entity);
                    cmdUI.AddTrigger(CommandTriggerFactory.Create(item as ToolStripButton));
                    return true;
                }
            }
            return false;
        }

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

        /// <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;
		}

        /// <summary>
        /// Add new tool button
        /// </summary>
        /// <param name="cui"></param>
        /// <param name="pathHint"></param>
        /// <returns></returns>
        public ToolStripItem AddNewButton(CommandUI cui, CommandPathHint pathHint) {
            ToolStrip target = FindBar(pathHint.Parent);
            if (target == null) {
                target = CreateNewToolBar(pathHint.Parent, DefaultTranspalentColor, new Size(16, 16));
            }
            // create and add to tool bar.
            ToolStripItem newitem = CreateToolStripItem(cui, target);
            newitem.Name = pathHint.ID;
            int n = FindInsertIndex(target.Items, pathHint);
            target.Items.Insert(n, newitem);
            return newitem;
        }

        /// <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);
            }
            return newitem;
        }

        protected Image GetIconImage(CommandUI cui) {
            int idx = cui.IconIndex;
            ImageRef iref = cui.Icon;
            if (idx == CommandUI.IconIndexUnassinged) {
                iref.AddRef();
                return iref.Image;
            } else {
                ImageList il;
                if (! imglistTable.TryGetValue(iref, out il)) {
                    iref.AddRef();
                    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;
            }
        }
        #endregion

        #region common protected functions
        /// <summary>
        /// get group name from a stripitem
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        protected string GetGroupName(ToolStripItem item)
		{
			return GetGroupName(item.Name);
		}

		/// <summary>
		/// split groupname prefix from item ID
		/// </summary>
		/// <param name="iid"></param>
		/// <returns></returns>
        protected string GetGroupName(string iid)
		{
            int n = iid.IndexOf(CommandManager.GroupSepalator);
            if (n == -1)
                return "";
            else
                return iid.Substring(0, n);
		}

		protected int FindInsertIndex(ToolStripItemCollection children, string bid, string after, string before){
            CommandPathHint hint = new CommandPathHint(bid, after, before);
            return FindInsertIndex(children, hint);
        }
        protected int FindInsertIndex(ToolStripItemCollection children, CommandPathHint hint)
		{
			string gn = GetGroupName(hint.ID);

			ToolStripItem ai=null;
			ToolStripItem bi=null;
            bool gaf = false;
            bool gbf = false;
            int start = 0;
            int end = children.Count;
            int gmin = -1;
            int gmax = end;
            for (int n = 0; n < children.Count; n++)
			{
                ToolStripItem item = children[n];
				if(item.Name==null) continue;
                String gn2 = GetGroupName(item);
                bool b = gn2.Equals(gn);
                if(b){
                    if (gmin<0) {
                        gmin = n;
                    }
                    gmax = n;
                }
                if (item.Name.Equals(hint.After)) {
                    ai = item;
                    gaf = b;
                    start = n;
                }
                if (item.Name.Equals(hint.Before)) {
                    bi = item;
                    gbf = b;
                    end = n;
                }
			}
            if (gmin >= 0) { // same group items found.
                if (!gaf) { // 'after' is null or not in the same group.
                    if (bi != null) { // 'before' is valid.
                        // 'end' points out the 'before' index.
                        // returns smaller index of which last group item or the 'before'
                        return Math.Min(gmax, end);
                    } else { // 'before' is NOT valid.
                        // returns last group index
                        return gmax + 1;
                    }
                }
                if (!gbf) { // 'before' is null ore not in tne same group.
                    if (ai != null) { // 'after' is valid.
                        // 'start' points out the 'after' index.
                        // returns larger index of which fitst group item or the 'after'
                        return Math.Max(gmin, start) + 1;
                    } else { // 'after' is NOT valid.
                        // returns last group index
                        return gmax + 1;
                    }
                } else {
                    // 'before' is valid and 'end' points out it's index.
                    return end;
                }
            } else { // same group items not found.
                int i;
                if (ai != null) {
                    gn = GetGroupName(ai);
                    end = children.Count;
                    for ( i = start+1; i < end; i++) {
                        ToolStripItem item = children[i];
                        // find first item which group is different from that of 'after'.
                        if (!GetGroupName(item).Equals(gn)) break;
                    }
                    // insert sepalator after 'after' group end.
                    children.Insert(i++, NewSepalator());
                    return i;
                }
                if (bi != null) {
                    gn = GetGroupName(bi);
                    end--;
                    for (i = end; i >0; i--) {
                        ToolStripItem item = children[i];
                        // find last item which group is different from that of 'before'.
                        if (!GetGroupName(item).Equals(gn)) break;
                    }
                    // insert sepalator before 'after' group end.
                    children.Insert(i, NewSepalator());
                    return i;
                }
                if (children.Count > 0) {
                    // insert sepalator after the last item.
                    children.Insert(end++, NewSepalator());
                }
                // returns last index.
                return end;
            }
		}

        protected int FindInsertIndex(ToolStripItemCollection children, string bid)
		{
			int idx=0;	
			bool find = false;
			string gn = GetGroupName(bid);

            for (int i = 0; i < children.Count; i++)
			{
                ToolStripItem item = children[i];
				if(item.Name==null) continue;

				if(GetGroupName(item).Equals(gn))
				{
					find = true;
					idx = i;
				}
				else if( find )			
					return idx;				
			}
			// insert sepalator
            if (!find && children.Count > 0)
                children.Add(NewSepalator());
            idx = children.Count;
			return idx;
		}

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

        #endregion

    }
}
