using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Collections.Generic;
using System.Windows.Forms;
namespace DevComponents.AdvTree
{
    /// 
    /// Represents collection for ColumnHeader objects.
    /// 
    public class ColumnHeaderCollection : CollectionBase
    {
        #region Private Variables
        private Node _ParentNode = null;
        private AdvTree _Parent = null;
        #endregion
        #region Internal Implementation
        /// 
        /// Default constructor.
        /// 
        public ColumnHeaderCollection()
        {
        }
        /// 
        /// Gets or sets the node this collection is associated with.
        /// 
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Node ParentNode
        {
            get { return _ParentNode; }
        }
        /// 
        /// Sets the node collection belongs to.
        /// 
        /// ColumnHeader that is parent of this collection.
        internal void SetParentNode(Node parent)
        {
            _ParentNode = parent;
        }
        internal AdvTree Parent
        {
            get { return _Parent; }
            set { _Parent = value; }
        }
        /// 
        /// Adds new object to the collection.
        /// 
        /// Object to add.
        /// Index of newly added object.
        public int Add(ColumnHeader ch)
        {
            return List.Add(ch);
        }
        /// 
        /// Returns reference to the object in collection based on it's index.
        /// 
        public ColumnHeader this[int index]
        {
            get { return (ColumnHeader)(List[index]); }
            set { List[index] = value; }
        }
        /// 
        /// Returns reference to the object in collection based on it's name.
        /// 
        public ColumnHeader this[string name]
        {
            get 
            {
                int index = IndexOf(name);
                if (index == -1) return null;
                return this[index];
            }
            set { this[IndexOf(name)] = value; }
        }
        /// 
        /// Inserts new object into the collection.
        /// 
        /// Position of the object.
        /// Object to insert.
        public void Insert(int index, ColumnHeader value)
        {
            List.Insert(index, value);
        }
        /// 
        /// Returns index of the object inside of the collection.
        /// 
        /// Reference to the object.
        /// Index of the object.
        public int IndexOf(ColumnHeader value)
        {
            return List.IndexOf(value);
        }
        /// 
        /// Returns index of the object inside of the collection.
        /// 
        /// Name of column to return index for.
        /// Index of the column or -1 if column not found.
        public int IndexOf(string name)
        {
            for (int i = 0; i < this.List.Count; i++)
            {
                if (this[i].Name == name) return i;
            }
            return -1;
        }
        /// 
        /// Returns index of the object inside of the collection based on column DataFieldName.
        /// 
        /// DataFieldName of column to return index for.
        /// Index of the column or -1 if column not found.
        public int IndexOfDataField(string dataFieldName)
        {
            dataFieldName = dataFieldName.ToLower();
            for (int i = 0; i < this.List.Count; i++)
            {
                if (this[i].DataFieldName.ToLower() == dataFieldName) return i;
            }
            return -1;
        }
        /// 
        /// Returns index of the object inside of the collection based on column DataFieldName.
        /// 
        /// DataFieldName of column to return index for.
        /// Index of the column or -1 if column not found.
        internal int IndexOfField(string fieldName)
        {
            fieldName = fieldName.ToLower();
            for (int i = 0; i < this.List.Count; i++)
            {
                ColumnHeader header = this[i];
                if (header.Tag is BindingMemberInfo && ((BindingMemberInfo)header.Tag).BindingField.ToLower() == fieldName)
                    return i;
                if (header.DataFieldName.ToLower() == fieldName) return i;
                if (header.Name == fieldName) return i;
            }
            return -1;
        }
        /// 
        /// Returns whether collection contains specified object.
        /// 
        /// Object to look for.
        /// true if object is part of the collection, otherwise false.
        public bool Contains(ColumnHeader value)
        {
            return List.Contains(value);
        }
        protected override void OnSet(int index, object oldValue, object newValue)
        {
            if (oldValue is ColumnHeader)
            {
                ColumnHeader header = (ColumnHeader)oldValue;
                if (header.SortDirection != eSortDirection.None) IsSorted = false;
            }
            if (newValue is ColumnHeader)
            {
                ColumnHeader header = (ColumnHeader)newValue;
                if (header.SortDirection != eSortDirection.None) IsSorted = true;
            }
            base.OnSet(index, oldValue, newValue);
        }
        /// 
        /// Removes specified object from the collection.
        /// 
        /// 
        public void Remove(ColumnHeader value)
        {
            List.Remove(value);
        }
        protected override void OnRemoveComplete(int index, object value)
        {
            if (value is ColumnHeader)
            {
                ColumnHeader header = (ColumnHeader)value;
                if (header.SortDirection != eSortDirection.None) IsSorted = false;
                header.HeaderSizeChanged -= new EventHandler(this.HeaderSizeChanged);
                header.SortCells -= new SortCellsEventHandler(SortCellsHandler);
                header.MouseDown -= new System.Windows.Forms.MouseEventHandler(ColumnMouseDown);
                header.MouseUp -= new System.Windows.Forms.MouseEventHandler(ColumnMouseUp);
                header.Parent = null;
            }
            InvalidateDisplayIndexes();
            UpdateTreeLayout();
            base.OnRemoveComplete(index, value);
        }
        protected override void OnInsertComplete(int index, object value)
        {
            if (value is ColumnHeader)
            {
                ((ColumnHeader)value).HeaderSizeChanged += new EventHandler(this.HeaderSizeChanged);
                ((ColumnHeader)value).SortCells += new SortCellsEventHandler(SortCellsHandler);
                ((ColumnHeader)value).MouseDown += new System.Windows.Forms.MouseEventHandler(ColumnMouseDown);
                ((ColumnHeader)value).MouseUp += new System.Windows.Forms.MouseEventHandler(ColumnMouseUp);
                ((ColumnHeader)value).Parent = this;
            }
            InvalidateDisplayIndexes();
            UpdateTreeLayout();
            base.OnInsertComplete(index, value);
        }
        [System.Reflection.Obfuscation(Exclude = true)]
        private void ColumnMouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            AdvTree tree = GetTree();
            if (tree != null)
                tree.InvokeColumnHeaderMouseUp(sender, e);
        }
        [System.Reflection.Obfuscation(Exclude = true)]
        private void ColumnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            AdvTree tree = GetTree();
            if (tree != null)
                tree.InvokeColumnHeaderMouseDown(sender, e);
        }
        private AdvTree GetTree()
        {
            AdvTree tree = _Parent;
            if (tree == null && _ParentNode != null)
                tree = _ParentNode.TreeControl;
            return tree;
        }
        /// 
        /// Occurs before the cells are sorted.
        /// 
        [Description("Occurs before the cells are sorted.")]
        public event SortCellsEventHandler SortCells;
        protected virtual void OnSortCells(SortEventArgs e)
        {
            if (SortCells != null)
                SortCells(this, e);
        }
        private void SortCellsHandler(object sender, SortEventArgs e)
        {
            ColumnHeader ch = (ColumnHeader)sender;
            int i = this.IndexOf(ch);
            IComparer comparer = null;
            if (e.ReverseSort)
            {
                if (ch.SortComparerReverse != null)
                    comparer = ch.SortComparerReverse;
                else
                    comparer = new NodeComparerReverse(i);
            }
            else
            {
                if (ch.SortComparer != null)
                    comparer = ch.SortComparer;
                else
                    comparer = new NodeComparer(i);
            }
            if (e.Comparer == null)
                e.Comparer = comparer;
            else
                comparer = e.Comparer;
            OnSortCells(e);
            if (e.Cancel)
                return;
            if (_Parent != null)
            {
                _Parent.Nodes.Sort(comparer);
            }
            else if (_ParentNode != null)
                _ParentNode.Nodes.Sort(comparer);
        }
        private bool _UpdatingSortDirection = false;
        /// 
        /// Called when SortDirection property on column header is set to value other than None.
        /// 
        /// Ref to column header
        internal void SortDirectionUpdated(ColumnHeader header)
        {
            if (_UpdatingSortDirection) return;
            _UpdatingSortDirection = true;
            try
            {
                if (header.SortDirection == eSortDirection.None)
                {
                    IsSorted = false;
                    return;
                }
                
                IsSorted = true;
                foreach (ColumnHeader col in this.List)
                {
                    if (col != header && col.SortDirection != eSortDirection.None)
                        col.SortDirection = eSortDirection.None;
                }
            }
            finally
            {
                _UpdatingSortDirection = false;
            }
        }
        private bool _IsSorted;
        /// 
        /// Gets whether a column that is part of this collection has SortDirection set.
        /// 
        public bool IsSorted
        {
            get { return _IsSorted; }
            internal set { _IsSorted = value; }
        }
        internal void UpdateSort()
        {
            if (!_IsSorted) return;
            foreach (ColumnHeader header in this.List)
            {
                if (header.SortDirection != eSortDirection.None)
                {
                    header.Sort(header.SortDirection == eSortDirection.Descending);
                    break;
                }
            }
        }
        private void UpdateTreeLayout()
        {
            if (_Parent != null)
            {
                _Parent.InvalidateNodesSize();
                _Parent.Invalidate();
                if (_Parent.Nodes.Count == 0)
                    _Parent.RecalcLayout();
                else
                    _Parent.SetPendingLayout();
            }
            else if (_ParentNode != null)
            {
                AdvTree tree = _ParentNode.TreeControl;
                if (tree != null)
                {
                    tree.InvalidateNodeSize(_ParentNode);
                    tree.Invalidate();
                }
            }
        }
        private void HeaderSizeChanged(object sender, EventArgs e)
        {
            UpdateTreeLayout();
        }
        /// 
        /// Copies collection into the specified array.
        /// 
        /// Array to copy collection to.
        /// Starting index.
        public void CopyTo(ColumnHeader[] array, int index)
        {
            List.CopyTo(array, index);
        }
        /// 
        /// Copies contained items to the ColumnHeader array.
        /// 
        /// Array to copy to.
        internal void CopyTo(ColumnHeader[] array)
        {
            List.CopyTo(array, 0);
        }
        protected override void OnClear()
        {
            foreach (ColumnHeader item in this)
            {
                item.HeaderSizeChanged -= new EventHandler(this.HeaderSizeChanged);
                item.SortCells -= new SortCellsEventHandler(SortCellsHandler);
                item.MouseDown -= new System.Windows.Forms.MouseEventHandler(ColumnMouseDown);
                item.MouseUp -= new System.Windows.Forms.MouseEventHandler(ColumnMouseUp);
                item.Parent = null;
            }
            IsSorted = false;
            base.OnClear();
        }
        /// 
        ///     A map of display index (key) to index in the column collection (value).  Used to quickly find a column from its display index.
        /// 
        internal List DisplayIndexMap
        {
            get
            {
                if (!_IsDisplayIndexValid)
                {
                    UpdateDisplayIndexMap();
                }
                return _DisplayIndexMap;
            }
        }
        /// 
        /// Gets the display index for specified column.
        /// 
        /// Column that is part f ColumnHeaderCollection
        /// Display index or -1 column is not part of this collection.
        public int GetDisplayIndex(ColumnHeader column)
        {
            int index = this.IndexOf(column);
            UpdateDisplayIndexMap();
            for (int i = 0; i < _DisplayIndexMap.Count; i++)
            {
                if (_DisplayIndexMap[i] == index) return i;
            }
            return -1;
        }
        /// 
        /// Returns the column that is displayed at specified display index..
        /// 
        /// 0 based display index.
        /// ColumnHeader
        public ColumnHeader ColumnAtDisplayIndex(int displayIndex)
        {
            UpdateDisplayIndexMap();
            return this[_DisplayIndexMap[displayIndex]];
        }
        private List _DisplayIndexMap = new List();
        private bool _IsDisplayIndexValid = false;
        private void UpdateDisplayIndexMap()
        {
            if (_IsDisplayIndexValid) return;
            _IsDisplayIndexValid = true;
            _DisplayIndexMap.Clear();
            List workingMap = new List();
            bool isAllDefault = true;
            for (int i = 0; i < Count; i++)
            {
                int displayIndex = this[i].DisplayIndex;
                if (displayIndex != -1) isAllDefault = false;
                workingMap.Add(new IndexToDisplayIndex(i, displayIndex));
            }
            if (!isAllDefault)
                workingMap.Sort(new DisplayIndexComparer());
            foreach (IndexToDisplayIndex item in workingMap)
            {
                _DisplayIndexMap.Add(item.Index);
            }
        }
        /// 
        /// Gets reference to last visible column or null if there is no last visible column.
        /// 
        public DevComponents.AdvTree.ColumnHeader LastVisibleColumn
        {
            get
            {
                List displayMap = DisplayIndexMap;
                for (int i = displayMap.Count - 1; i >=0; i--)
                {
                    if (this[displayMap[i]].Visible) return this[displayMap[i]];
                }
                return null;
            }
        }
        /// 
        /// Gets reference to first visible column or null if there is no first visible column.
        /// 
        public DevComponents.AdvTree.ColumnHeader FirstVisibleColumn
        {
            get
            {
                List displayMap = DisplayIndexMap;
                for (int i = 0; i < displayMap.Count; i++)
                {
                    if (this[displayMap[i]].Visible) return this[displayMap[i]];
                }
                return null;
            }
        }
        #region IndexToDisplayIndex Class
        private class IndexToDisplayIndex
        {
            public int Index = -1;
            public int DisplayIndex = -1;
            /// 
            /// Initializes a new instance of the IndexToDisplayIndex class.
            /// 
            /// 
            /// 
            public IndexToDisplayIndex(int index, int displayIndex)
            {
                Index = index;
                DisplayIndex = displayIndex;
            }
        }
        #endregion
        #region DisplayIndexComparer
        private class DisplayIndexComparer : IComparer
        {
            #region IComparer Members
            public int Compare(IndexToDisplayIndex x, IndexToDisplayIndex y)
            {
                if (x.DisplayIndex == y.DisplayIndex)
                {
                    return x.Index - y.Index;
                }
                else
                {
                    return x.DisplayIndex - y.DisplayIndex;
                }
            }
            #endregion
        }
        #endregion
        internal void DisplayIndexChanged(ColumnHeader column, int newDisplayIndex, int oldDisplayIndex)
        {
            InvalidateDisplayIndexes();
            if (_UpdatingDisplayIndexes) return;
            if (_Parent != null && !_Parent.IsUpdateSuspended)
                UpdateTreeLayout();
            else if (_ParentNode != null && _ParentNode.TreeControl != null && !_ParentNode.TreeControl.IsUpdateSuspended)
                UpdateTreeLayout();
        }
        /// 
        /// Invalidates the display indexes and causes them to be re-evaluated on next layout.
        /// 
        public void InvalidateDisplayIndexes()
        {
            _IsDisplayIndexValid = false;
        }
        #endregion
        private Rectangle _Bounds = Rectangle.Empty;
        internal void SetBounds(System.Drawing.Rectangle totalBounds)
        {
            _Bounds = totalBounds;
        }
        /// 
        /// Gets the column header rendering bounds.
        /// 
        [Browsable(false)]
        public Rectangle Bounds
        {
            get { return _Bounds; }
        }
        internal bool UsesRelativeSize = false;
        private bool _UpdatingDisplayIndexes = false;
        internal bool UpdatingDisplayIndexes
        {
            get { return _UpdatingDisplayIndexes; }
            set { _UpdatingDisplayIndexes = value; }
        }
    }
}