using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing.Design;
namespace DevComponents.DotNetBar.SuperGrid
{
    /// 
    /// Defines the collection of grid columns
    /// 
    [Editor("DevComponents.SuperGrid.Design.GridColumnCollectionEditor, DevComponents.SuperGrid.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=26d81176cfa2b486", typeof(UITypeEditor))]
    public class GridColumnCollection : CollectionBase
    {
        #region Events
        /// 
        /// Occurs when the collection has changed
        /// 
        [Description("Occurs when collection has changed.")]
        public CollectionChangeEventHandler CollectionChanged;
        #endregion
        #region Private Variables
        private GridElement _ParentItem;
        private int[] _DisplayIndexMap;
        private bool _IsDisplayIndexValid;
        #endregion
        #region Public properties
        #region FirstSelectableColumn
        /// 
        /// Gets reference to first selectable column
        /// or null if there is no first selectable column.
        /// 
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public GridColumn FirstSelectableColumn
        {
            get
            {
                int[] displayMap = DisplayIndexMap;
                for (int i = 0; i < displayMap.Length; i++)
                {
                    GridColumn column = this[displayMap[i]];
                    if (column.Visible == true && column.AllowSelection == true)
                        return (column);
                }
                return (null);
            }
        }
        #endregion
        #region FirstVisibleColumn
        /// 
        /// Gets reference to first visible column
        /// or null if there is no first visible column.
        /// 
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public GridColumn FirstVisibleColumn
        {
            get
            {
                int[] displayMap = DisplayIndexMap;
                for (int i = 0; i < displayMap.Length; i++)
                {
                    if (this[displayMap[i]].Visible)
                        return (this[displayMap[i]]);
                }
                return (null);
            }
        }
        #endregion
        #region Index indexer
        /// 
        /// Returns reference to the object in collection based on it's index.
        /// 
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public GridColumn this[int index]
        {
            get { return (GridColumn)(List[index]); }
            set { List[index] = value; }
        }
        #endregion
        #region Name indexer
        ///
        /// Name indexer
        ///
        ///
        ///
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public GridColumn this[string name]
        {
            get
            {
                int index = FindIndexByName(name);
                return (index >= 0 ? this[index] : null);
            }
            set
            {
                int index = FindIndexByName(name);
                if (index < 0)
                    throw new Exception("Column Name not defined (" + name + ").");
                List[index] = value;
            }
        }
        #region FindIndexByName
        private int FindIndexByName(string name)
        {
            for (int i=0; i
        /// Gets reference to last selectable column
        /// or null if there is no last selectable column.
        /// 
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public GridColumn LastSelectableColumn
        {
            get
            {
                int[] displayMap = DisplayIndexMap;
                for (int i = displayMap.Length - 1; i >= 0; i--)
                {
                    GridColumn column = this[displayMap[i]];
                    if (column.Visible == true && column.AllowSelection == true)
                        return (column);
                }
                return (null);
            }
        }
        #endregion
        #region LastVisibleColumn
        /// 
        /// Gets reference to last visible column
        /// or null if there is no last visible column.
        /// 
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public GridColumn LastVisibleColumn
        {
            get
            {
                int[] displayMap = DisplayIndexMap;
                for (int i = displayMap.Length - 1; i >= 0; i--)
                {
                    if (this[displayMap[i]].Visible)
                        return (this[displayMap[i]]);
                }
                return (null);
            }
        }
        #endregion
        #region ParentItem
        /// 
        /// Gets or sets the node this collection is associated with.
        /// 
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public GridElement ParentItem
        {
            get { return _ParentItem; }
            internal set
            {
            	 _ParentItem = value;
                 OnParentItemChanged();
            }
        }
        private void OnParentItemChanged()
        {
        }
        #endregion
        #endregion
        #region Add
        /// 
        /// Adds new object to the collection.
        /// 
        /// Object to add.
        /// Index of newly added object.
        public int Add(GridColumn value)
        {
            value.ColumnIndex = List.Add(value);
            return (value.ColumnIndex);
        }
        #endregion
        #region Insert
        /// 
        /// Inserts new object into the collection.
        /// 
        /// Position of the object.
        /// Object to insert.
        public void Insert(int index, GridColumn value)
        {
            List.Insert(index, value);
            for (int i = 0; i < Count; i++)
                ((GridColumn)List[i]).ColumnIndex = i;
        }
        #endregion
        #region IndexOf
        /// 
        /// Returns index of the object inside of the collection.
        /// 
        /// Reference to the object.
        /// Index of the object.
        public int IndexOf(GridColumn value)
        {
            return (List.IndexOf(value));
        }
        #endregion
        #region Contains
        /// 
        /// Returns whether collection contains specified object.
        /// 
        /// Object to look for.
        /// true if object is part of the collection, otherwise false.
        public bool Contains(GridColumn value)
        {
            return (List.Contains(value));
        }
        /// 
        /// Returns whether collection contains specified object.
        /// 
        /// Name of the object to look for.
        /// true if object is part of the collection, otherwise false.
        public bool Contains(string name)
        {
            foreach (GridColumn column in List)
            {
                if (column.Name.Equals(name) == true)
                    return (true);
            }
            return (false);
        }
        #endregion
        #region Remove
        /// 
        /// Removes specified object from the collection.
        /// 
        /// 
        public void Remove(GridColumn value)
        {
            List.Remove(value);
            for (int i = 0; i < Count; i++)
                ((GridColumn)List[i]).ColumnIndex = i;
        }
        #endregion
        #region OnRemoveComplete
        /// 
        /// Called when remove of an item is completed.
        /// 
        /// Index of removed item
        /// Removed item.
        protected override void OnRemoveComplete(int index, object value)
        {
            if (value is GridColumn)
            {
                GridColumn column = (GridColumn)value;
                GridPanel panel = column.GridPanel;
                ResetColumn(panel, column);
            }
            OnCollectionChanged(CollectionChangeAction.Remove, value);
            base.OnRemoveComplete(index, value);
        }
        #endregion
        #region Clear
        /// 
        /// Clears all objects from the collection.
        /// 
        public new void Clear()
        {
            foreach (GridColumn column in this)
                ResetColumn(column.GridPanel, column);
            List.Clear();
        }
        #endregion
        #region OnClearComplete
        /// 
        /// OnClearComplete
        /// 
        protected override void OnClearComplete()
        {
            InvalidateDisplayIndexes();
            InvalidateLayout();
            base.OnClearComplete();
        }
        #endregion
        #region ResetColumn
        private void ResetColumn(GridPanel panel, GridColumn column)
        {
            column.DisplayIndexChanged -= ColumnDisplayIndexChanged;
            if (panel.DataSource != null)
                panel.DataBinder.DataResetCount++;
            GridCell cell = column.SuperGrid.ActiveElement as GridCell;
            if (cell != null)
            {
                if (cell.ColumnIndex == column.ColumnIndex)
                    column.SuperGrid.ActiveElement = null;
            }
            if (panel.SortColumns.Contains(column))
                panel.SortColumns.Remove(column);
            if (panel.GroupColumns.Contains(column))
                panel.GroupColumns.Remove(column);
            FilterPanel fp = column.SuperGrid.ActiveFilterPanel;
            if (fp != null && fp.GridColumn == column)
                fp.EndEdit();
        }
        #endregion
        #region OnInsertComplete
        /// 
        /// Called when insertion of an item is completed.
        /// 
        /// Insert index.
        /// Inserted item.
        protected override void OnInsertComplete(int index, object value)
        {
            if (value is GridColumn)
            {
                GridColumn column = (GridColumn)value;
                if (column.Parent != null && column.Parent != _ParentItem)
                    throw new Exception("Column is already a member of another Column collection.");
                column.DisplayIndexChanged += ColumnDisplayIndexChanged;
                column.Parent = _ParentItem;
            }
            OnCollectionChanged(CollectionChangeAction.Add, value);
            base.OnInsertComplete(index, value);
        }
        #endregion
        #region OnCollectionChanged
        private void OnCollectionChanged(
            CollectionChangeAction action, object value)
        {
            for (int i = 0; i < Count; i++)
                ((GridColumn)List[i]).ColumnIndex = i;
            if (action == CollectionChangeAction.Remove)
            {
                GridColumn rcol = (GridColumn)value;
                GridPanel panel = rcol.GridPanel;
                if (rcol.DisplayIndex >= 0)
                {
                    foreach (GridColumn col in panel.Columns)
                    {
                        if (col.DisplayIndex > rcol.DisplayIndex)
                            col.DisplayIndex--;
                    }
                }
            }
            InvalidateDisplayIndexes();
            InvalidateLayout();
            if (CollectionChanged != null)
            {
                CollectionChangeEventArgs e = new
                    CollectionChangeEventArgs(action, value);
                CollectionChanged(this, e);
            }
        }
        #endregion
        #region ColumnDisplayIndexChanged
        private void ColumnDisplayIndexChanged(object sender, EventArgs e)
        {
            InvalidateDisplayIndexes();
            InvalidateLayout();
        }
        #endregion
        #region InvalidateDisplayIndexes
        /// 
        /// Invalidates the display indexes and
        /// causes them to be re-evaluated on next layout.
        /// 
        public void InvalidateDisplayIndexes()
        {
            _IsDisplayIndexValid = false;
        }
        #endregion
        #region InvalidateLayout
        private void InvalidateLayout()
        {
            if (_ParentItem != null)
            {
                _ParentItem.InvalidateLayout();
                if (_ParentItem.SuperGrid != null)
                {
                    _ParentItem.SuperGrid.NeedMergeLayout = true;
                    _ParentItem.SuperGrid.DisplayedMergeLayoutCount++;
                }
            }
        }
        #endregion
        #region CopyTo
        /// 
        /// Copies collection into the specified array.
        /// 
        /// Array to copy collection to.
        /// Starting index.
        public void CopyTo(GridColumn[] array, int index)
        {
            List.CopyTo(array, index);
        }
        /// 
        /// Copies contained items to the ColumnHeader array.
        /// 
        /// Array to copy to.
        internal void CopyTo(GridColumn[] array)
        {
            List.CopyTo(array, 0);
        }
        #endregion
        #region OnClear
        /// 
        /// Called when collection is cleared.
        /// 
        protected override void OnClear()
        {
            if (Count > 0)
            {
                GridPanel panel = this[0].Parent as GridPanel;
                if (panel != null)
                {
                    panel.SuperGrid.ActiveElement = null;
                    foreach (GridElement item in panel.Rows)
                    {
                        GridRow row = item as GridRow;
                        if (row != null)
                        {
                            foreach (GridCell cell in row.Cells)
                            {
                                cell.EditControl = null;
                                cell.RenderControl = null;
                            }
                        }
                    }
                }
            }
            foreach (GridColumn item in this)
            {
                item.EditControl = null;
                item.RenderControl = null;
            }
            base.OnClear();
        }
        #endregion
        #region DisplayIndexMap
        /// 
        /// A map of display index (key) to index in the column collection
        /// (value). Used to quickly find a column from its display index.
        /// 
        internal int[] DisplayIndexMap
        {
            get
            {
                if (!_IsDisplayIndexValid)
                    UpdateDisplayIndexMap();
                return (_DisplayIndexMap);
            }
        }
        #endregion
        #region GetDisplayIndex
        /// 
        /// Gets the display index for specified column.
        /// 
        /// Column that is part of ColumnHeaderCollection
        /// Display index or -1 column is not part of this collection.
        public int GetDisplayIndex(GridColumn column)
        {
            return (GetDisplayIndex(IndexOf(column)));
        }
        ///
        /// Gets the display index for specified column.
        ///
        ///Column index
        ///Display index or -1 column is not part of this collection.
        public int GetDisplayIndex(int index)
        {
            UpdateDisplayIndexMap();
            for (int i = 0; i < _DisplayIndexMap.Length; i++)
            {
                if (_DisplayIndexMap[i] == index)
                    return (i);
            }
            return (-1);
        }
        #endregion
        #region ColumnAtDisplayIndex
        /// 
        /// Returns the column that is displayed at specified display index.
        /// 
        /// 0 based display index.
        /// ColumnHeader
        public GridColumn ColumnAtDisplayIndex(int displayIndex)
        {
            UpdateDisplayIndexMap();
            return (this[_DisplayIndexMap[displayIndex]]);
        }
        #endregion
        #region UpdateDisplayIndexMap
        private void UpdateDisplayIndexMap()
        {
            if (_IsDisplayIndexValid == false)
            {
                _IsDisplayIndexValid = true;
                _DisplayIndexMap = new int[Count];
                for (int i = 0; i < Count; i++)
                    _DisplayIndexMap[i] = -1;
                for (int i = 0; i < Count; i++)
                {
                    int di = this[i].DisplayIndex;
                    if (di != -1)
                        AddIndexToMap(i, di);
                }
                int n = 0;
                for (int i = 0; i < Count; i++)
                {
                    int di = this[i].DisplayIndex;
                    if (di == -1)
                        AddIndexToMap(i, n++);
                }
            }
        }
        private void AddIndexToMap(int i, int di)
        {
            for (int j = 0; j < Count; j++)
            {
                int n = (di + j) % Count;
                if (_DisplayIndexMap[n] == -1)
                {
                    _DisplayIndexMap[n] = i;
                    break;
                }
            }
        }
        #endregion
        #region GetNextVisibleColumn
        /// 
        /// Gets reference to next visible column
        /// or null if there is no next visible column
        /// 
        public GridColumn GetNextVisibleColumn(GridColumn column)
        {
            return (GetNextVisibleColumn(GetDisplayIndex(column)));
        }
        internal GridColumn GetNextVisibleColumn(int displayIndex)
        {
            int[] displayMap = DisplayIndexMap;
            while (++displayIndex < Count)
            {
                if (this[displayMap[displayIndex]].Visible)
                    return (this[displayMap[displayIndex]]);
            }
            return (null);
        }
        #endregion
        #region GetPrevVisibleColumn
        /// 
        /// Gets reference to previous visible column
        /// or null if there is no last visible previous column
        /// 
        public GridColumn GetPrevVisibleColumn(GridColumn column)
        {
            return (GetPrevVisibleColumn(GetDisplayIndex(column)));
        }
        internal GridColumn GetPrevVisibleColumn(int displayIndex)
        {
            int[] displayMap = DisplayIndexMap;
            while (--displayIndex >= 0)
            {
                if (this[displayMap[displayIndex]].Visible)
                    return (this[displayMap[displayIndex]]);
            }
            return (null);
        }
        #endregion
        #region GetLastVisibleFrozenColumn
        /// 
        /// Gets reference to the last visible frozen column
        /// or null if there is none
        /// 
        public GridColumn GetLastVisibleFrozenColumn()
        {
            int[] displayMap = DisplayIndexMap;
            if (displayMap.Length > 0)
            {
                GridPanel panel = this[displayMap[0]].GridPanel;
                if (panel != null &&
                    panel.FrozenColumnCount > 0 && panel.IsSubPanel == false)
                {
                    for (int i = displayMap.Length - 1; i >= 0; i--)
                    {
                        GridColumn column = this[displayMap[i]];
                        if (column.Visible == true)
                        {
                            if (column.IsHFrozen == true)
                                return (column);
                        }
                    }
                }
            }
            return (null);
        }
        #endregion
    }
}