//------------------------------------------------------------------------------
// <copyright file=”*.cs” company=”EhLib Team”>
//     Copyright (c) 2017 Dmitry V. Bolshakov   
// </copyright>
//------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
// ReSharper disable VirtualMemberCallInConstructor

namespace EhLib
{

  public enum TreeListNodeAttachMode
  {
    Add,
    AddFirst,
    AddChild,
    AddChildFirst,
    Insert,
    AddAfter
  }

  public enum TreeListAddMode
  {
    AddFirst,
    Add,
    Insert
  }

  public enum TreeListNotification
  {
    NodeAdded,
    NodeDeleted,
    TreeListChanged
  }

  public delegate int CompareNodes(BaseTreeNode node1, BaseTreeNode node2, object paramSort);

  public class TreeList : object
  {
    //private Type itemClass = null;
    private int maxLevel = 1000;
    //private TTreeNodeNotifyEvent FOnExpandedChanged = null;
    //private TTreeNodeNotifyResultEvent FOnExpandedChanging = null;
    private BaseTreeNode root;

    public TreeList(BaseTreeNode rootNode)
    {
      root = rootNode;
      root.Parent = null;
      //root.level = 0;
      root.TreeList = this;
    }

    protected internal int MaxLevel
    {
      get { return maxLevel; }
      set { maxLevel = value; }
    }

    protected internal BaseTreeNode Root
    {
      get { return root; }
      set { root = value; }
    }

    protected internal BaseTreeNode AddChild(BaseTreeNode parent)
    {
      BaseTreeNode result;
      BaseTreeNode parentNode;
      if (parent == null)
      {
        parentNode = root;
      }
      else
      {
        parentNode = parent;
      }
      result = CreateNodeApart();
      AddNode(result, parentNode, TreeListNodeAttachMode.AddChild, true);

      return result;
    }

    protected void DeleteChildren(BaseTreeNode node)
    {
      node.Clear();
    }

    protected internal void DeleteNode(BaseTreeNode node, bool reIndex)
    {
      DeleteChildren(node);
      ExtractNode(node, reIndex);
    }

    protected internal void ExtractNode(BaseTreeNode node, bool reIndex)
    {
      bool buildChildrenIndexRequired;
      int deletedIndex;
      BaseTreeNode oldParentNode;

      if (node.Parent == null)
        return;
      buildChildrenIndexRequired = (node.Index < node.Parent.Items.Count - 1);
      deletedIndex = node.Index;
      oldParentNode = node.Parent;
      node.Parent.Delete(node.Index);
      if (reIndex)
      {
        if (buildChildrenIndexRequired)
          BuildChildrenIndex(oldParentNode, false);
      }
      TreeChanged(node, TreeListNotification.NodeDeleted, deletedIndex, oldParentNode);
    }

    public void Expand(BaseTreeNode node, bool recurse)
    {
      if (node == null) node = root;
      if (node.Items.Count > 0)
      {
        if (node != root)
          node.Expanded = true;
        if (recurse)
        {
          for (int i = 0; i < node.Items.Count; i++)
            Expand(node.Items[i], true);
        }
      }
    }

    public void Collapse(BaseTreeNode node, bool recurse)
    {
      if (node == null)
        node = root;
      node.Expanded = false;
      if (recurse)
        for (int i = 0; i < node.Items.Count; i++)
          Collapse(node.Items[i], true);
    }

    public void AddNode(BaseTreeNode node, BaseTreeNode destination, TreeListNodeAttachMode mode, bool reIndex)
    {
      if ((node == null) || (node == root)) return;
      if (destination == null)
        destination = root;
      if ((destination == root) &&
          (mode != TreeListNodeAttachMode.AddChild) &&
          (mode != TreeListNodeAttachMode.AddChildFirst))
        return;

      if (node.TreeList == null)
        node.TreeList = this;
      else
      {
        if (node.Parent != null)
          OnAddingAdoptedNode(node);
      }

      switch (mode)
      {
        case TreeListNodeAttachMode.AddChild:
          {
            node.Parent = destination;
            destination.HasChildren = true;
            node.Index = destination.Add(node);
            //Node.SetLevel(Destination.Level + 1);
            break;
          }
        case TreeListNodeAttachMode.AddChildFirst:
          {
            node.Parent = destination;
            destination.HasChildren = true;
            destination.Insert(0, node);
            node.Index = 0;
            //Node.SetLevel(Destination.Level + 1);
            if (reIndex)
              BuildChildrenIndex(node.Parent, false);
            break;
          }
        case TreeListNodeAttachMode.Add:
          {
            AddNode(node, destination.Parent, TreeListNodeAttachMode.AddChild, false);
            break;
          }
        case TreeListNodeAttachMode.AddFirst:
          {
            AddNode(node, destination.Parent, TreeListNodeAttachMode.AddChildFirst, reIndex);
            break;
          }
        case TreeListNodeAttachMode.Insert:
          {
            node.Parent = destination.Parent;
            destination.Parent.HasChildren = true;
            destination.Parent.Insert(destination.Index, node);
            //node.Index = destination.Index;
            //Node.SetLevel(Destination.Parent.Level + 1);
            //if (reIndex) BuildChildrenIndex(destination.Parent, false, node.Index);
            break;
          }
        case TreeListNodeAttachMode.AddAfter:
          {
            if (destination.Parent.Items[destination.Parent.Items.Count - 1] == destination)
              AddNode(node, destination, TreeListNodeAttachMode.Add, reIndex);
            else
              AddNode(node, destination.Parent.Items[destination.Index + 1], TreeListNodeAttachMode.Insert, reIndex);
            break;
          }
      }
      TreeChanged(node, TreeListNotification.NodeAdded, -1, null);
    }

    public void MoveTo(BaseTreeNode node, BaseTreeNode destination, TreeListNodeAttachMode mode, bool reIndex)
    {
      if ((node == null) | (node == root))
        return;
      if ((destination == root) &&
          (mode != TreeListNodeAttachMode.AddChild) &&
          (mode != TreeListNodeAttachMode.AddChildFirst))
        return;
      if (destination.HasParentOf(node))
        throw new InvalidOperationException("Reference-loop found");
      BaseTreeNode oldParent = node.Parent;
      node.Parent.Delete(node.Index);
      if ((mode == TreeListNodeAttachMode.AddChild) && (reIndex == true))
        BuildChildrenIndex(oldParent, false);
      node.Parent.HasChildren = (node.Parent.Items.Count > 0);
      AddNode(node, destination, mode, reIndex);
    }

    public BaseTreeNode FindNodeByData(BaseTreeNode startNode, object data)
    {
      int i;
      BaseTreeNode curNode;
      BaseTreeNode result = null;
      if (startNode == null)
        startNode = root;
      for (i = 0; i <= startNode.Items.Count - 1; i++)
      {
        curNode = startNode.Items[i];
        if (curNode.Data == data)
        {
          result = curNode;
          break;
        }
        else
        {
          result = FindNodeByData(curNode, data);
          if (result != null)
            break;
        }
      }
      return result;
    }

    public BaseTreeNode GetPrevSibling(BaseTreeNode node)
    {
      if ((node == null) || (node.Index == 0) || (node.Parent == null))
        return null;
      else
        return node.Parent.Items[node.Index - 1];
    }

    public BaseTreeNode GetNextSibling(BaseTreeNode node)
    {
      if (node == null || node.Parent == null || node.Index == node.Parent.Items.Count - 1)
        return null;
      else
        return node.Parent.Items[node.Index + 1];
    }

    public BaseTreeNode GetFirstChild(BaseTreeNode node)
    {
      if ((node == null) || (node.Items.Count == 0))
        return null;
      else
        return node.Items[0];
    }

    public BaseTreeNode GetLastChild(BaseTreeNode node)
    {
      if ((node == null) || (node.Items.Count == 0))
        return null;
      else
        return node.Items[node.Items.Count - 1];
    }

    public BaseTreeNode GetFirst()
    {
      return GetFirstChild(Root);
    }

    public BaseTreeNode GetPrevious(BaseTreeNode node)
    {
      BaseTreeNode prevSiblingNode;
      BaseTreeNode result = node;

      if ((result == null) || (result == Root))
        return result;

      prevSiblingNode = GetPrevSibling(result);

      if (prevSiblingNode != null)
      {
        result = GetLast(prevSiblingNode);
        if (result == null)
          result = prevSiblingNode;
      }
      else if (node.Parent != Root)
        result = node.Parent;
      else
        result = null;
      return result;
    }

    public BaseTreeNode GetNext(BaseTreeNode node)
    {
      BaseTreeNode firstChild;
      BaseTreeNode nextSibling;
      BaseTreeNode result = node;

      if ((result == null) || (result == root))
        return result;
      firstChild = GetFirstChild(result);
      if (firstChild != null)
      {
        return firstChild;
      }

      do
      {
        nextSibling = GetNextSibling(result);
        if (nextSibling != null)
        {
          result = nextSibling;
          break;
        }
        else
        {
          if (result.Parent != root)
            result = result.Parent;
          else
          {
            result = null;
            break;
          }
        }
      }
      while (true);

      return result;
    }

    public BaseTreeNode GetLast(BaseTreeNode node)
    {
      BaseTreeNode result;
      BaseTreeNode next;

      if (node == null)
        node = root;
      result = GetLastChild(node);
      while (result != null)
      {
        next = GetLastChild(result);
        if (next == null)
          break;
        result = next;
      }
      return result;
    }

    public bool IsHasChildren(BaseTreeNode node = null)
    {
      if (node == null)
        node = root;
      return (node.Items.Count > 0);
    }

    public int CountChildren(BaseTreeNode node)
    {
      if (node == null)
        node = root;
      return node.Items.Count;
    }

    public BaseTreeNode GetParentAtLevel(BaseTreeNode node, int parentLevel)
    {
      BaseTreeNode result;
      if ((node == null) | (node == root))
        return null;
      if ((parentLevel >= node.Level) || (parentLevel < 0))
        return null;
      if (parentLevel == 0)
      {
        return root;
      }
      result = node;
      while (result != null)
      {
        result = result.Parent;
        if (result != null)
          if (result.Level == parentLevel)
            break;
      }
      return result;
    }

    public BaseTreeNode GetFirstVisible()
    {
      BaseTreeNode curNode;
      BaseTreeNode result;

      if (!IsHasChildren())
        return null;
      curNode = GetFirstChild(root);
      if (curNode == null)
        return null;
      result = curNode;
      if (!result.Visible)
      {
        while (true)
        {
          curNode = GetNextSibling(result);
          if (curNode != null)
          {
            result = curNode;
            if (result.Visible)
              break;
          }
          else
          {
            if (result.Parent != root)
            {
              result = result.Parent;
            }
            else
            {
              result = null;
              break;
            }
          }
        }
      }
      return result;
    }

    public bool GetPathVisible(BaseTreeNode node, bool considerCollapsed)
    {
      if ((node == null) | (node == root)) return false;
      do
      {
        node = node.Parent;
      } while ((node != root) && (node.Expanded && considerCollapsed) && node.Visible);

      return (node == root);
    }

    public BaseTreeNode GetParentVisible(BaseTreeNode node, bool considerCollapsed)
    {
      BaseTreeNode result = node;
      while (result != root)
      {
        do
        {
          result = result.Parent;
        } while (!result.Expanded && considerCollapsed);

        if ((result == root) ||
            (result.Visible && GetPathVisible(result, considerCollapsed)))
          break;

        while ((result != root) && (result.Parent.Expanded || !considerCollapsed))
        {
          result = result.Parent;
        }

      }
      return result;
    }

    public BaseTreeNode GetNextVisible(BaseTreeNode node, bool considerCollapsed)
    {
      BaseTreeNode result = node;
      BaseTreeNode firstChild;
      BaseTreeNode nextSibling;
      bool forceSearch;

      if (result != null)
      {
        if (result == root)
        {
          return null;
        }

        if (!(result.Visible) || !(GetPathVisible(result, considerCollapsed)))
          result = GetParentVisible(result, considerCollapsed);

        firstChild = GetFirstChild(result);

        if ((result.Expanded | !considerCollapsed) & (firstChild != null))
        {
          result = firstChild;
          forceSearch = false;
        }
        else
        {
          forceSearch = true;
        }

        if (forceSearch || !(result.Visible))
        {
          do
          {
            nextSibling = GetNextSibling(result);
            if (nextSibling != null)
            {
              result = nextSibling;
              if (result.Visible) break;
            }
            else
            {
              if (result.Parent != root)
                result = result.Parent;
              else
              {
                result = null;
                break;
              }
            }
          }
          while (true);
        }
      }

      return result;
    }

    public void Clear()
    {
      DeleteChildren(root);
    }

    public void BuildChildrenIndex(BaseTreeNode node, bool recurse, int fromIndex = -1, int toIndex = -1)
    {
      {
        int i;
        BaseTreeNode curNode;
        int fIndex;
        int tIndex;

        if (node == null)
          //node = root;
          throw new InvalidOperationException("BuildChildrenIndex for node == null");
        node.VisibleItems.ItemsIternal.Clear();
        if (fromIndex == -1)
          fIndex = 0;
        else
          fIndex = fromIndex;
        if (toIndex == -1)
          tIndex = node.Items.Count - 1;
        else
          tIndex = toIndex;
        for (i = fIndex; i <= tIndex; i++)
        {
          curNode = node.Items[i];
          curNode.Index = i;
          if (recurse)
            BuildChildrenIndex(curNode, true);
        }
        node.BuildVisibleItems();
      }
    }

    protected virtual void ExportToTreeView(TreeView treeView, BaseTreeNode node, TreeNode nodeTree, bool addChild)
    {
    }

    //public void QuickSort(int L, int R, TCompareNodesEh Compare)
    //{
    //}

    //public void SortData(TCompareNodesEh CompareProg, Object ParamSort, bool ARecurse)
    //{
    //}

    protected virtual int CompareTreeNodes(BaseTreeNode node1, BaseTreeNode node2, object paramSort)
    {
      int result = 0;
      return result;
    }

    public BaseTreeNode GetNextVisibleSibling(BaseTreeNode node)
    {
      BaseTreeNode result;
      if (node.Parent.Items.Count == node.Parent.VisibleItems.Count)
      {
        result = GetNextSibling(node);
      }
      else
      {
        if (node.Parent == null || (node.VisibleIndex == node.Parent.VisibleItems.Count - 1))
        {
          return null;
        }
        result = node.Parent.VisibleItems[node.VisibleIndex + 1];
      }
      return result;
    }

    public BaseTreeNode GetPrevVisibleSibling(BaseTreeNode node)
    {
      BaseTreeNode result;
      if (node.Parent.Items.Count == node.Parent.VisibleItems.Count)
      {
        result = GetPrevSibling(node);
      }
      else
      {
        if (node.Parent == null || (node.VisibleIndex == 0))
        {
          return null;
        }
        result = node.Parent.VisibleItems[node.VisibleIndex - 1];
      }
      return result;
    }

    internal protected virtual void ExpandedChanged(BaseTreeNode node)
    {
    }

    internal protected virtual void TreeChanged(BaseTreeNode node, TreeListNotification operation, int oldIndex, BaseTreeNode oldParentNode)
    {
    }

    internal protected virtual bool ExpandedChanging(BaseTreeNode node)
    {
      return false;
    }

    public virtual BaseTreeNode CreateNodeApart()
    {
      BaseTreeNode result = new BaseTreeNode
      {
        Parent = null,
        TreeList = this,
        Index = -1
      };
      //result.level = 0;
      return result;
    }

    protected virtual void OnAddingAdoptedNode(BaseTreeNode node)
    {
      throw new InvalidOperationException("An attempt to adopt an adopted child: " + node.ToString());
    }

    protected internal virtual void NodeVisibleChanged(BaseTreeNode node)
    {
      
    }
  }

  public class BaseTreeNode : object
  {

    private bool expanded;
    private bool hasChildren;
    private bool hasVisibleChildren;
    private int index;
    //    private ArrayList FItems = null;
    private BaseTreeNodeItems items;
    private BaseTreeNodeItems visibleItems;
    //internal int level = 0;
    private TreeList treeList;
    private BaseTreeNode parent;
    //private string text = String.Empty;
    private bool visible;
    private int visibleCount;
    private int visibleIndex;
    private bool visibleItemsObsolete;
    private bool itemsIndexesObsolete;

    public BaseTreeNode()
    {
      // inherited Create(AOwner);
      visible = true;
    }

    public object Data { get; set; }

    public BaseTreeNodeItems Items
    {
      get
      {
        if (items == null)
          items = CreateItemsList();
        return items;
      }
    }

    public BaseTreeNodeItems VisibleItems
    {
      get
      {
        EnsureVisibleItems();
        if (visibleItemsObsolete)
          InternalBuildVisibleItems();
        return visibleItems;
      }
    }

    public bool Expanded
    {
      get
      {
        return expanded;
      }
      set
      {
        SetExpanded(value);
      }
    }

    public bool HasChildren
    {
      get
      {
        return hasChildren;
      }
      set
      {
        hasChildren = value;
      }
    }

    public bool HasVisibleChildren
    {
      get
      {
        return GetHasVisibleChildren();
      }
      set
      {
        hasVisibleChildren = value;
      }
    }

    public int Index
    {
      get
      {
        if (Parent != null && Parent.itemsIndexesObsolete)
        {
          Parent.ResetItemsIndexes();
        }
        return index;
      }
      internal set { index = value; }
    }

    public int Level
    {
      get
      {
        return GetCalcLevel(this, 0);
      }
    }

    public TreeList TreeList
    {
      get
      {
        if (treeList == null)
          treeList = FetchTreeList();

        return treeList;
      }

      internal set
      {
        treeList = value;
      }
    }

    public BaseTreeNode Parent
    {
      get
      {
        return parent;
      }
      set
      {
        parent = value;
      }
    }

    public virtual bool Visible
    {
      get
      {
        return visible;
      }

      set
      {
        SetVisible(value);
      }
    }

    public int VisibleIndex
    {
      get
      {
        if (Parent.visibleItemsObsolete)
        {
          Parent.EnsureVisibleItems();
          Parent.InternalBuildVisibleItems();
        }
        return visibleIndex;
      }
    }

    // BaseTreeNode.Methods
    protected virtual BaseTreeNodeItems CreateVisibleItemsList()
    {
      return new BaseTreeNodeItems(this);
    }

    protected virtual BaseTreeNodeItems CreateItemsList()
    {
      return new BaseTreeNodeItems(this);
    }

    protected void Exchange(int index1, int index2)
    {
      if (index1 == index2)
      {
        return;
      }
      BaseTreeNode item = Items[index1];
      Items.ItemsIternal[index1] = Items[index2];
      Items.ItemsIternal[index2] = item;
      Items[index2].index = index2;
      Items[index1].index = index1;

      itemsIndexesObsolete = true;
    }

    protected int GetCount()
    {
      int result;
      result = Items.Count;
      return result;
    }

    public virtual void BuildVisibleItems()
    {
      visibleItemsObsolete = true;
    }

    private void InternalBuildVisibleItems()
    {
      if (visibleItems == null) return;

      int i;
      visibleItems.ItemsIternal.Clear();
      for (i = 0; i < Items.Count; i++)
      {
        if (Items[i].Visible)
        {
          visibleItems.ItemsIternal.Add(Items[i]);
          Items[i].visibleIndex = visibleItems.Count - 1;
        }
      }
      visibleCount = visibleItems.Count;
      visibleItemsObsolete = false;
      // if (Count > 0) {and HasChildren} then
      HasVisibleChildren = (visibleItems.Count > 0);
    }

    private void ResetItemsIndexes()
    {
      for (int i = 0; i < Items.Count; i++)
        Items[i].Index = i;
      itemsIndexesObsolete = false;
    }

    private bool GetHasVisibleChildren()
    {
      bool result;
      if (visibleItemsObsolete)
      {
        InternalBuildVisibleItems();
      }
      result = hasVisibleChildren;
      return result;
    }

    protected void QuickSort(int l, int r, CompareNodes compare, object paramSort)
    {
      int i;
      int j;
      BaseTreeNode p;
      do
      {
        i = l;
        j = r;
        p = Items[(l + r) >> 1];
        do
        {
          while (compare(Items[i], p, paramSort) < 0)
          {
            i++;
          }
          while (compare(Items[j], p, paramSort) > 0)
          {
            j -= 1;
          }
          if (i <= j)
          {
            Exchange(i, j);
            i++;
            j -= 1;
          }
        } while (!(i > j));
        if (l < j)
        {
          QuickSort(l, j, compare, paramSort);
        }
        l = i;
      } while (!(i >= r));

      if (visibleCount != Items.Count)
      {
        BuildVisibleItems();
      }
      // Owner.BuildChildrenIndex(Self, False); // To reset visible index.

    }

    private void SetExpanded(bool value)
    {
      if (expanded == value)
      {
        return;
      }
      if (ExpandedChanging())
      {
        expanded = value;
        ExpandedChanged();
      }
    }

    private void SetVisible(bool value)
    {
      if (visible != value)
      {
        if (VisibleChangeable())
        {
          visible = value;
          VisibleChanged();
        }
      }
    }

    public virtual void SortData(CompareNodes compareProg, object paramSort, bool recurse)
    {
      int i;
      if (Items.Count == 0)
      {
        return;
      }
      QuickSort(0, Items.Count - 1, compareProg, paramSort);
      if (recurse)
      {
        for (i = 0; i < Items.Count; i++)
        {
          Items[i].SortData(compareProg, paramSort, true);
        }
      }
      // Owner.BuildChildrenIndex(Self, False);

    }

    public virtual void SetItemsOrder(IEnumerable<BaseTreeNode> orderEnumerable)
    {
      List<BaseTreeNode> orderedList = new List<BaseTreeNode>(orderEnumerable);

      foreach (BaseTreeNode node in orderEnumerable)
      {
        if (Items.IndexOf(node) == -1)
          throw new InvalidOperationException("SetItemsOrder: Item is not in the Items list");
      }

      foreach (BaseTreeNode node in Items)
      {
        if (orderedList.IndexOf(node) == -1)
          throw new InvalidOperationException("SetItemsOrder: Item is not in the Ordered list");
      }

      Items.ItemsIternal.Clear();

      foreach (BaseTreeNode node in orderEnumerable)
      {
        Items.ItemsIternal.Add(node);
      }

      ChildVisibleChanged(null);
      itemsIndexesObsolete = true;
      if (TreeList != null)
        TreeList.TreeChanged(this, TreeListNotification.TreeListChanged, -1, null);
    }

    public virtual void ExpandedChanged()
    {
      if (TreeList != null)
      {
        TreeList.ExpandedChanged(this);
      }
    }

    public virtual bool ExpandedChanging()
    {
      if (TreeList != null)
        return TreeList.ExpandedChanging(this);
      else
        return true;
    }

    public virtual void VisibleChanged()
    {
      // if Visible then
      if (Parent != null)
        Parent.ChildVisibleChanged(this);
    }

    public virtual void ChildVisibleChanged(BaseTreeNode node)
    {
      BuildVisibleItems();
      if (TreeList != null)
        TreeList.NodeVisibleChanged(node);
    }

    public virtual bool VisibleChangeable()
    {
      return true;
    }

    //internal protected void SetLevel(int ALevel)
    //{
    //  int i;
    //  if (level != ALevel)
    //  {
    //    if (ALevel > TreeList.MaxLevel)
    //    {
    //      throw new InvalidOperationException("TBaseTreeNodeEh.SetLevel: Max level exceed - " + (TreeList.MaxLevel).ToString());
    //    }
    //    level = ALevel;
    //    for (i = 0; i < Items.Count; i++)
    //    {
    //      items[i].SetLevel(level + 1);
    //    }
    //  }
    //}

    protected internal int Add(BaseTreeNode node)
    {
      int result;
      if (/*Item.Owner != null &&*/ node.TreeList != TreeList)
      {
        throw new InvalidOperationException("TBaseTreeNodeEh.Add: Tree nodes can not have different Owners");
      }

      //Item.owner = Owner;

      if ((visibleCount == Items.Count) && node.Visible)
      {
        Items.ItemsIternal.Add(node);
        result = Items.Count - 1;
        node.visibleIndex = result;
        visibleCount++;
        BuildVisibleItems();
      }
      else
      {
        Items.ItemsIternal.Add(node);
        result = Items.Count - 1;
        BuildVisibleItems();
      }
      return result;
    }

    public virtual void Clear()
    {
      int i;
      for (i = 0; i < Items.Count; i++)
      {
        Items[i].Clear();
      }

      VisibleItems.ItemsIternal.Clear();

      while (Items.Count > 0)
        Items.RemoveAt(Items.Count - 1);
      //items.items.Clear();
    }

    protected internal void Delete(int index)
    {
      BaseTreeNode node = Items[index];
      if (index < Items.Count - 1)
        itemsIndexesObsolete = true;
      Items.ItemsIternal.RemoveAt(index);
      node.parent = null;
      node.treeList = null;
      node.index = -1;
      node.visibleIndex = -1;
      HasChildren = (Items.Count > 0);

      BuildVisibleItems();
    }

    protected internal void Insert(int index, BaseTreeNode node)
    {
      if (node.TreeList != TreeList)
      {
        throw new InvalidOperationException("TBaseTreeNodeEh.Add: Tree nodes can not has different Owners");
      }
      if (index != Items.Count)
        itemsIndexesObsolete = true;
      if ((visibleCount == Items.Count) && node.Visible)
      {
        Items.ItemsIternal.Insert(index, node);
        visibleCount++;
        BuildVisibleItems();
      }
      else
      {
        Items.ItemsIternal.Insert(index, node);
        BuildVisibleItems();
      }
    }

    protected internal bool HasParentOf(BaseTreeNode node)
    {
      BaseTreeNode nd;
      nd = this;
      while (nd != TreeList.Root)
      {
        if (nd == node)
        {
          return true;
        }
        nd = nd.Parent;
      }
      return false;
    }

    protected internal int GetCalcLevel(BaseTreeNode node, int nodeLevel)
    {
      if (node.Parent != null)
        return GetCalcLevel(node.Parent, nodeLevel + 1);
      else
        return nodeLevel;
    }

    private TreeList FetchTreeList()
    {
      if (treeList != null)
        return treeList;
      else if (Parent != null)
        return Parent.FetchTreeList();
      else
        return null;
    }

    protected internal virtual void OnAddingAdoptedNode(BaseTreeNode node)
    {
      throw new InvalidOperationException("An attempt to adopt an adopted child: " + node.ToString());
    }

    private void EnsureVisibleItems()
    {
      if (visibleItems == null)
        visibleItems = CreateVisibleItemsList();
    }

  }

  public class BaseTreeNodeItems : object, IList
  {
    internal readonly List<BaseTreeNode> ItemsIternal;
    internal readonly BaseTreeNode OwnerNodeInternal;

    public BaseTreeNodeItems(BaseTreeNode ownerNode)
    {
      this.ItemsIternal = new List<BaseTreeNode>();
      this.OwnerNodeInternal = ownerNode;
    }

    //props
    public int Count
    {
      get { return ItemsIternal.Count; }
    }

    public BaseTreeNode this[int index]
    {
      get { return ItemsIternal[index]; }
    }

    public bool IsReadOnly
    {
      get { return false; }
    }

    //methods
    public virtual int Add(BaseTreeNode node)
    {
      return this.AddInternal(node);
    }

    public int IndexOf(BaseTreeNode node)
    {
      for (int i = 0; i < this.Count; i++)
      {
        if (this[i] == node)
        {
          return i;
        }
      }
      return -1;
    }

    public void CopyTo(Array array, int index)
    {
      ((ICollection)ItemsIternal).CopyTo(array, index);
    }

    public IEnumerator GetEnumerator()
    {
      return ItemsIternal.GetEnumerator();
    }

    public bool Contains(BaseTreeNode value)
    {
      return (this.IndexOf(value) != -1);
    }

    public void Clear()
    {
      this.OwnerNodeInternal.Clear();
    }

    public void Remove(BaseTreeNode node)
    {
      if (OwnerNodeInternal.TreeList != null)
      {
        OwnerNodeInternal.TreeList.ExtractNode(node, true);
      }
      else
      {
        int nodeIndex = ItemsIternal.IndexOf(node);
        if (nodeIndex >= 0)
          RemoveAt(nodeIndex);
      }
    }

    public void RemoveAt(int index)
    {
      BaseTreeNode node = ItemsIternal[index];

      if (OwnerNodeInternal.TreeList != null)
      {
        OwnerNodeInternal.TreeList.ExtractNode(node, true);
      }
      else
      {
        OwnerNodeInternal.Delete(index);
      }
    }

    public void Insert(int index, BaseTreeNode node)
    {
      if (node == null)
      {
        throw new ArgumentNullException("node");
      }

      if (index == Count)
      {
        Add(node);
      }
      else if (OwnerNodeInternal.TreeList != null)
      {
        BaseTreeNode nodeBefore = ItemsIternal[index];
        OwnerNodeInternal.TreeList.AddNode(node, nodeBefore, TreeListNodeAttachMode.Insert, true);
      }
      else
      {
        node.Parent = OwnerNodeInternal;
        node.TreeList = OwnerNodeInternal.TreeList;
        ItemsIternal.Insert(index, node);
        node.Index = index;
        OwnerNodeInternal.HasChildren = true;
        OwnerNodeInternal.BuildVisibleItems();
      }
    }

    private int AddInternal(BaseTreeNode node)
    {
      if (node == null)
      {
        throw new ArgumentNullException("node");
      }

      if (OwnerNodeInternal.TreeList != null)
      {
        OwnerNodeInternal.TreeList.AddNode(node, OwnerNodeInternal, TreeListNodeAttachMode.AddChild, true);
      }
      else
      {
        if (node.Parent != null)
          OwnerNodeInternal.OnAddingAdoptedNode(node);
        node.Parent = OwnerNodeInternal;
        node.TreeList = OwnerNodeInternal.TreeList;
        ItemsIternal.Add(node);
        node.Index = ItemsIternal.Count - 1;
        OwnerNodeInternal.HasChildren = true;
        OwnerNodeInternal.BuildVisibleItems();
      }

      return node.Index;
    }

    internal void Sort(Comparison<BaseTreeNode> comparison)
    {
      ItemsIternal.Sort(comparison);
      //ResetIndexes();
    }

    //IList
    bool IList.IsFixedSize
    {
      get
      {
        return false;
      }
    }

    object IList.this[int index]
    {
      get
      {
        return this[index];
      }
      set
      {
        throw new ArgumentException("Assign BaseTreeNodeItems IList.this[int index] is not supported");

        //if (!(value is BaseTreeNode))
        //{
        //  throw new ArgumentException("TreeNodeCollectionBadTreeNode", "value");
        //}
        //this[index] = (BaseTreeNode)value;
      }
    }

    int IList.Add(object node)
    {
      if (node == null)
      {
        throw new ArgumentNullException("node");
      }

      BaseTreeNode btNode = node as BaseTreeNode;
      if (btNode != null)
        return this.Add(btNode);
      else
        throw new ArgumentException("now is not BaseTreeNode");
    }

    bool IList.Contains(object node)
    {
      BaseTreeNode treeNode = node as BaseTreeNode;
      return (treeNode != null && this.Contains(treeNode));
    }

    int IList.IndexOf(object node)
    {
      BaseTreeNode baseNode = (node as BaseTreeNode);
      if (baseNode != null)
      {
        return this.IndexOf(baseNode);
      }
      return -1;
    }

    void IList.Insert(int index, object node)
    {
      if (!(node is BaseTreeNode))
      {
        throw new ArgumentException("TreeNodeCollectionBadTreeNode");
      }
      this.Insert(index, (BaseTreeNode)node);
    }

    void IList.Remove(object node)
    {
      BaseTreeNode treeNode = node as BaseTreeNode;
      if (treeNode != null)
      {
        this.Remove(treeNode);
      }
    }

    //ICollection
    bool ICollection.IsSynchronized
    {
      get
      {
        return false;
      }
    }

    object ICollection.SyncRoot
    {
      get
      {
        return this;
      }
    }
  }

  public class TreeListGeneric<T> : TreeList where T : BaseTreeNode, new()
  {

    public TreeListGeneric() : base(new T())
    {
    }

    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public new T Root
    {
      get { return (T)base.Root; }
      set { base.Root = value; }
    }

    protected T AddChild(T node)
    {
      return (T)base.AddChild(node);
    }

    protected void DeleteChildren(T node)
    {
      node.Clear();
    }

    protected void DeleteNode(T node, bool reIndex)
    {
      base.DeleteNode(node, reIndex);
    }

    protected void ExtractNode(T node, bool reIndex)
    {
      base.ExtractNode(node, reIndex);
    }

    public void Expand(T node, bool recurse)
    {
      base.Expand(node, recurse);
    }

    public void Collapse(T node, bool recurse)
    {
      base.Collapse(node, recurse);
    }

    public void AddNode(T node, T destination, TreeListNodeAttachMode mode, bool reIndex)
    {
      base.AddNode(node, destination, mode, reIndex);
    }

    public void MoveTo(T node, T destination, TreeListNodeAttachMode mode, bool reIndex)
    {
      base.MoveTo(node, destination, mode, reIndex);
    }

    public T FindNodeByData(T startNode, object data)
    {
      return (T)base.FindNodeByData(startNode, data);
    }

    public T GetPrevSibling(T node)
    {
      return (T)base.GetPrevSibling(node);
    }

    public T GetNextSibling(T node)
    {
      return (T)base.GetNextSibling(node);
    }

    public T GetFirstChild(T node)
    {
      return (T)base.GetFirstChild(node);
    }

    public T GetLastChild(T node)
    {
      return (T)base.GetLastChild(node);
    }

    public new T GetFirst()
    {
      return (T)base.GetFirst();
    }

    public T GetPrevious(T node)
    {
      return (T)base.GetPrevious(node);
    }

    public T GetNext(T node)
    {
      return (T)base.GetNext(node);
    }

    public T GetLast(T node)
    {
      return (T)base.GetLast(node);
    }

    public bool IsHasChildren(T node = null)
    {
      return base.IsHasChildren(node);
    }

    public int CountChildren(T node)
    {
      return base.CountChildren(node);
    }

    public T GetParentAtLevel(T node, int parentLevel)
    {
      return (T)base.GetParentAtLevel(node, parentLevel);
    }

    public new T GetFirstVisible()
    {
      return (T)base.GetFirstVisible();
    }

    public bool GetPathVisible(T node, bool considerCollapsed)
    {
      return base.GetPathVisible(node, considerCollapsed);
    }

    public T GetParentVisible(T node, bool considerCollapsed)
    {
      return (T)base.GetParentVisible(node, considerCollapsed);
    }

    public T GetNextVisible(T node, bool considerCollapsed)
    {
      return (T)base.GetNextVisible(node, considerCollapsed);
    }

    public new void Clear()
    {
      base.Clear();
    }

    public void BuildChildrenIndex(T node = null, bool recurse = true, int fromIndex = -1, int toIndex = -1)
    {
      base.BuildChildrenIndex(node, recurse, fromIndex, toIndex);
    }

    public void ExportToTreeView(TreeView treeView, T node, TreeNode nodeTree, bool addChild)
    {
      base.ExportToTreeView(treeView, node, nodeTree, addChild);
    }

    //public void QuickSort(int L, int R, TCompareNodesEh Compare)
    //{
    //}

    //public void SortData(TCompareNodesEh CompareProg, Object ParamSort, bool ARecurse)
    //{
    //}

    public int CompareTreeNodes(T node1, T node2, object paramSort)
    {
      return base.CompareTreeNodes(node1, node2, paramSort);
    }

    public T GetNextVisibleSibling(T node)
    {
      return (T)base.GetNextVisibleSibling(node);
    }

    public T GetPrevVisibleSibling(T node)
    {
      return (T)base.GetPrevVisibleSibling(node);
    }

    public void ExpandedChanged(T node)
    {
      base.ExpandedChanged(node);
    }

    protected virtual void TreeChanged(T node, TreeListNotification operation, int oldIndex, T oldParentNode)
    {
      //base.TreeChanged(node, Operation, OldIndex, OldParentNode);
    }

    internal protected override void TreeChanged(BaseTreeNode node, TreeListNotification operation, int oldIndex, BaseTreeNode oldParentNode)
    {
      TreeChanged((T)node, operation, oldIndex, (T)oldParentNode);
    }

    public bool ExpandedChanging(T node)
    {
      return base.ExpandedChanging(node);
    }

    public new virtual T CreateNodeApart()
    {
      return (T)base.CreateNodeApart();
    }

    protected virtual void OnAddingAdoptedNode(T node)
    {
      throw new InvalidOperationException("An attempt to adopt an adopted child: " + node.ToString());
    }

    protected override void OnAddingAdoptedNode(BaseTreeNode node)
    {
      OnAddingAdoptedNode((T)node);
    }

    protected internal override void NodeVisibleChanged(BaseTreeNode node)
    {
      NodeVisibleChanged((T)node);
    }

    protected virtual void NodeVisibleChanged(T node)
    {

    }
  }

  public class BaseTreeNodeItemsGeneric<T> : BaseTreeNodeItems where T : BaseTreeNode
  {
    public BaseTreeNodeItemsGeneric(BaseTreeNode ownerNode) : base(ownerNode)
    {

    }

    public new T this[int index]
    {
      get { return (T)base[index]; }
    }

    public new T OwnerNode
    {
      get { return (T)base.OwnerNodeInternal; }
    }

    //methods

// base methods
    public virtual int Add(T node)
    {
      return base.Add(node);
    }

    public int IndexOf(T node)
    {
      return base.IndexOf(node);
    }

    public bool Contains(T value)
    {
      return base.Contains(value);
    }

    public void Remove(T node)
    {
      base.Remove(node);
    }

    public void Insert(int index, T node)
    {
      base.Insert(index, node);
    }

    public void CopyTo(T[] array, int index)
    {
      ((ICollection)this).CopyTo(array, index);
    }

    public T[] ToArray()
    {
      T[] array = new T[Count];
      CopyTo(array, 0);
      return array;
    }
  }
}
