using System;
using System.Collections.Generic;
using System.Text;

namespace nft.util {
    public class TreeDataMap<T> : TreeNode<T> {
        public TreeDataMap(int child_capacity) : base(child_capacity) { }
    }

    public class TreeNode<T> {
        public delegate void NodeEventHandler(TreeNode<T> source, T oldData, TreeNode<T> related);
        public event NodeEventHandler NodeDataChangedEvent;
        public event NodeEventHandler ChildAddedEvent;
        public event NodeEventHandler ChildRemovedEvent;

        protected List<TreeNode<T>> children;
        protected TreeNode<T> parent;
        protected T data;
        
        internal protected TreeNode(int children_capacity) {
            children = new List<TreeNode<T>>(children_capacity);            
        }

        public int ChildrenCount {
            get { return children.Count; }
        }

        public IEnumerator<TreeNode<T>> GetChildren() {
            return children.GetEnumerator();
        }

        public TreeNode<T> Parent {
            get { return parent; }
        }

        /// <summary>
        /// sort children 
        /// </summary>
        /// <param name="comparer"></param>
        public void SortChildren(IComparer<T> comparer, bool enter_children) {
            TreeNodeComparer comp = new TreeNodeComparer(comparer);
            SortInternal(comp, enter_children);
        }

        protected void SortInternal(TreeNodeComparer comp, bool enter_children) {
            children.Sort(comp);
            if (enter_children) {
                foreach (TreeNode<T> node in children) {
                    node.SortInternal(comp, enter_children);
                }
            }
        }

        /// <summary>
        /// Add child node
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public TreeNode<T> AddChild(T data){
            TreeNode < T >  node =new TreeNode<T>(children.Capacity);
            node.Data = data;
            AddChild(node);
            return node;
        }

        internal protected bool AddChild(TreeNode<T> child) {
            if (child.parent != null) {
                if (child.parent == this)
                    return false;
                child.parent.Remove(child);
            }
            child.parent = this;
            children.Add(child);
            FireChildAddedEvent(this, child);
            return true;
        }


        /// <summary>
        /// Delete this node and children
        /// </summary>
        public void Delete() {
            children.Clear();
            if (parent != null) {
                parent.Remove(this);
            }
        }

        /// <summary>
        /// Remove child node
        /// </summary>
        /// <param name="child"></param>
        /// <returns></returns>
        public bool Remove(TreeNode<T> child) {
            bool ret = children.Remove(child);
            if (child.parent == this) {
                child.parent = null;
            }
            if (ret) {
                FireChildRemovedEvent(this, child);
            }
            return ret;
        }

        /// <summary>
        /// Associated Data
        /// </summary>
        public T Data {
            get { return data; }
            set {
                T old = default(T);
                if (data != null) {
                    if (!data.Equals(value)) {
                        old = data;
                        data = value;
                        FireDataChangedEvent(this, old);
                    }
                } else if(value!=null){
                    data = value;
                    FireDataChangedEvent(this, old);
                }
            }
        }

        /// <summary>
        /// returns true if there is a node which contains specified data
        /// </summary>
        /// <param name="data"></param>
        /// <param name="enter_children"></param>
        /// <returns></returns>
        public bool Contains(T data, bool enter_children) {
            if (enter_children) {
                foreach (TreeNode<T> node in children) {
                    if (data.Equals(node.Data) || node.Contains(data, true)) {
                        return true;
                    }
                }
            } else {
                foreach (TreeNode<T> node in children) {
                    if (data.Equals(node.Data)) {
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// returns the node which contains specified data
        /// </summary>
        /// <param name="data"></param>
        /// <param name="enter_children"></param>
        /// <returns></returns>
        public TreeNode<T> FindNode(T data, bool enter_children) {
            TreeNode<T> ret = null;
            if (enter_children) {
                foreach (TreeNode<T> node in children) {
                    if (data.Equals(node.Data) || node.Contains(data, true)) {
                        ret = node;
                        break;
                    }
                }
            } else {
                foreach (TreeNode<T> node in children) {
                    if (data.Equals(node.Data)) {
                        ret = node;
                        break;
                    }
                }
            }
            return ret;
        }

        /// <summary>
        /// move to new parent
        /// </summary>
        /// <param name="newparent"></param>
        /// <returns></returns>
        public bool Move(TreeNode<T> newparent){
            return newparent.AddChild(this);
        }

        protected void FireDataChangedEvent(TreeNode<T> source, T old) {
            if (NodeDataChangedEvent != null) {
                NodeDataChangedEvent(source, old, null);
            }
            if (parent != null) {
                parent.FireDataChangedEvent(source, old);
            }
        }

        protected void FireChildAddedEvent(TreeNode<T> source, TreeNode<T> child) {
            if (ChildAddedEvent != null) {
                ChildAddedEvent(source, default(T), child);
            }
            if (parent != null) {
                parent.ChildAddedEvent(source, default(T), child);
            }
        }

        protected void FireChildRemovedEvent(TreeNode<T> source, TreeNode<T> child) {
            if (ChildRemovedEvent != null) {
                ChildRemovedEvent(source, default(T), child);
            }
            if (parent != null) {
                parent.ChildRemovedEvent(source, default(T), child);
            }
        }

        /// <summary>
        /// wraps given IComparer to comparer each Data of TreeNodes
        /// </summary>
        protected class TreeNodeComparer : IComparer<TreeNode<T>> {
            private IComparer<T> comp;
            internal TreeNodeComparer(IComparer<T> comp) {
                this.comp = comp;
            }

            #region IComparer<TreeNode<T>> メンバ
            public int Compare(TreeNode<T> x, TreeNode<T> y) {
                return comp.Compare(x.Data, y.Data);
            }
            #endregion
        }
    }
}
