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.command {
    public class CommandPathHelper {
        public const char PathSepalator = '\\';
        public const char GroupSepalator = '|';
        public enum SepalatorRequirement { NONE = 0, BEFORE, AFTER };

        public static string[] SplitIntoNode(string path) {
            return path.Split(new char[] { CommandPathHelper.PathSepalator });
        }

        public static string CombinePath(string parent, string child) {
            if (parent[parent.Length-1] == CommandPathHelper.PathSepalator) {
                if (child[0] == CommandPathHelper.PathSepalator) {
                    child = child.Substring(1);
                }
                return parent+child;
            }
            return parent+CommandPathHelper.PathSepalator+child;
        }

    }

    public class CommandPathHelper<T> : CommandPathHelper where T : class {
        protected Func<T, string> GetItemId;

        public CommandPathHelper(Func<T, string> ItemIdGetter)
		{
            GetItemId = ItemIdGetter;
        }


        /// <summary>
        /// Determine insertion index 
        /// </summary>
        /// <param name="children">which to scan between</param>
        /// <param name="iid">itemId</param>
        /// <param name="after">null or itemId of which to be inserted after</param>
        /// <param name="before">null or itemId of which to be inserted before</param>
        /// <returns></returns>
        public int FindInsertIndex(IList children, string iid, string after, string before, out SepalatorRequirement spRequire) {
            CommandPathHint hint = new CommandPathHint(iid, after, before);
            return FindInsertIndex(children, hint, out spRequire);
        }

        /// <summary>
        /// Determine insertion index 
        /// </summary>
        /// <param name="children">which to scan between</param>
        /// <param name="hint"></param>
        /// <returns></returns>
        public int FindInsertIndex(IList children, CommandPathHint hint, out SepalatorRequirement spRequire)
		{
            spRequire = SepalatorRequirement.NONE;
            string gn = hint.GroupName;

			T ai=null;
			T 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++)
			{
                T item = children[n] as T;
                if (item == null) continue; // maybe it's a sepalator;
                String iid = GetItemId(item);
                if (iid == null) continue;
                String gn2 = CommandPathHint.SplitGroupName(iid);
                bool b = gn2.Equals(gn);
                if(b){
                    if (gmin<0) {
                        gmin = n;
                    }
                    gmax = n;
                }
                if (iid.Equals(hint.After)) {
                    ai = item;
                    gaf = b;
                    start = n;
                }
                if (iid.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++) {
                        T item = children[i] as T;
                        // find first item which group is different from that of 'after'.
                        if (!GetGroupName(item).Equals(gn)) break;
                    }
                    // insert sepalator after 'after' group end.
                    spRequire = SepalatorRequirement.AFTER;
                    return i;
                }
                if (bi != null) {
                    gn = GetGroupName(bi);
                    end--;
                    for (i = end; i >0; i--) {
                        T item = children[i] as T;
                        // find last item which group is different from that of 'before'.
                        if (!GetGroupName(item).Equals(gn)) break;
                    }
                    // insert sepalator before 'after' group end.
                    spRequire = SepalatorRequirement.BEFORE;
                    return i;
                }
                if (children.Count > 0) {
                    // insert sepalator after the last item (and should be before the inserted item).
                    spRequire = SepalatorRequirement.BEFORE;
                }
                // returns last index.
                return end;
            }
		}

        /// <summary>
        /// Determine insertion index from itemId only.
        /// </summary>
        /// <param name="children"></param>
        /// <param name="iid">ItemId of inserted item.</param>
        /// <returns></returns>
        public int FindInsertIndex(IList children, string iid, out SepalatorRequirement spRequire)
		{
			int idx=0;	
			bool find = false;
			string gn = CommandPathHint.SplitGroupName(iid);
            spRequire = SepalatorRequirement.NONE;

            for (int i = 0; i < children.Count; i++)
			{
                T item = children[i] as T;
                if (item == null) continue; // maybe it's a sepalator;
                String iid2 = GetItemId(item);
                if (iid2 == null) continue;
                // 同じグループの最後に挿入
				if(GetGroupName(item).Equals(gn))
				{
					find = true;
					idx = i;
				}
				else if( find )			
					return idx;				
			}
            // 同じグループが発見できなかったら最後に挿入
            if (!find && children.Count > 0)
                spRequire = SepalatorRequirement.BEFORE;
            idx = children.Count;
			return idx;
		}

        protected string GetGroupName(T item) {
            return CommandPathHint.SplitGroupName(GetItemId(item));
        }
    }
}
