using System;
using System.Drawing;
namespace DevComponents.DotNetBar.SuperGrid
{
    /// 
    /// Defines the collection of Virtual Rows
    /// 
    public class GridVirtualRows
    {
        #region Private Variables
        private GridContainer _Container;
        private const int MaxRows = 50;
        private const int MaxBuckets = 100;
        private int _BucketHead;
        private VPageBucket _LastAccessedBucket;
        private VPageBucket[] _PageBuckets;
        #endregion
        ///
        /// GridVirtualRowList
        ///
        ///
        public GridVirtualRows(GridContainer container)
        {
            _Container = container;
            _PageBuckets = new VPageBucket[MaxBuckets];
            _PageBuckets[0] = new VPageBucket(_Container, 0);
        }
        #region Public properties
        #region MaxRowIndex
        ///
        /// MaxRowIndex
        ///
        ///
        public int MaxRowIndex
        {
            get { return (GetMaxRowIndex()); }
            set { TruncateRowIndex(value); }
        }
        #region TruncateRowIndex
        private void TruncateRowIndex(int index)
        {
            _LastAccessedBucket = null;
            for (int i = 0; i < MaxBuckets; i++)
            {
                int n = (_BucketHead + i) % MaxBuckets;
                if (_PageBuckets[n] != null)
                {
                    if (_PageBuckets[n].StartIndex >= index)
                        _PageBuckets[n] = null;
                    else if (_PageBuckets[n].EndIndex > index)
                    {
                        for (int j = 0; j < MaxRows; j++)
                        {
                            if (_PageBuckets[n].StartIndex + j > index)
                                _PageBuckets[n].Rows[j] = null;
                        }
                    }
                }
            }
        }
        #endregion
        #region GetMaxRowIndex
        private int GetMaxRowIndex()
        {
            int maxIndex = 0;
            for (int i = 0; i < MaxBuckets; i++)
            {
                int n = (_BucketHead + i) % MaxBuckets;
                VPageBucket bucket = _PageBuckets[n];
                if (bucket != null)
                {
                    int index = bucket.StartIndex;
                    if (index >= maxIndex)
                    {
                        foreach (GridRow row in bucket.Rows)
                        {
                            if (row != null)
                            {
                                if (row.GridIndex > maxIndex)
                                    maxIndex = row.GridIndex;
                            }
                        }
                    }
                }
            }
            return (maxIndex);
        }
        #endregion
        #endregion
        #region Row Indexer
        ///
        /// Row indexer
        ///
        ///
        public GridRow this[int index]
        {
            get { return (GetRow(index)); }
        }
        #endregion
        #endregion
        #region FindBucket
        private VPageBucket FindBucket(int index)
        {
            if (_LastAccessedBucket != null)
            {
                if (_LastAccessedBucket.Contains(index))
                    return (_LastAccessedBucket);
            }
            for (int i = 0; i < MaxBuckets; i++)
            {
                int n = (_BucketHead + i) % MaxBuckets;
                if (_PageBuckets[n] != null)
                {
                    if (_PageBuckets[n].Contains(index))
                        return (_PageBuckets[n]);
                }
            }
            return (null);
        }
        #endregion
        #region FreeRow
        ///
        /// FreeRow
        ///
        ///
        ///
        ///
        public void FreeRow(int index)
        {
            VPageBucket bucket = FindBucket(index);
            if (bucket != null)
            {
                GridRow[] rows = bucket.Rows;
                GridRow row = rows[index - bucket.StartIndex];
                if (row != null)
                {
                    row.Dispose();
                    rows[index - bucket.StartIndex] = null;
                }
            }
        }
        #endregion
        #region IsCachedRow
        ///
        /// Gets whether the row is currently cached
        ///
        public bool IsCachedRow(int index)
        {
            if (_LastAccessedBucket != null)
            {
                if (_LastAccessedBucket.Contains(index) == true)
                    return (_LastAccessedBucket.IsCached(index));
            }
            for (int i = 0; i < MaxBuckets; i++)
            {
                int n = (_BucketHead + i) % MaxBuckets;
                if (_PageBuckets[n] != null)
                {
                    if (_PageBuckets[n].Contains(index) == true)
                        return (_PageBuckets[n].IsCached(index));
                }
            }
            return (false);
        }
        #endregion
        #region GetRow
        private GridRow GetRow(int index)
        {
            if (_LastAccessedBucket != null)
            {
                if (_LastAccessedBucket.Contains(index))
                    return (_LastAccessedBucket[index]);
            }
            for (int i = 0; i < MaxBuckets; i++)
            {
                int n = (_BucketHead + i) % MaxBuckets;
                if (_PageBuckets[n] == null)
                    return (NewBucketRow(n, index));
                if (_PageBuckets[n].Contains(index))
                {
                    _LastAccessedBucket = _PageBuckets[n];
                    return (_LastAccessedBucket[index]);
                }
            }
            int bucket = _BucketHead;
            GridPanel panel = _Container.GridPanel;
            if (panel != null)
            {
                for (int i = 0; i < MaxBuckets; i++)
                {
                    bucket = (_BucketHead + i) % MaxBuckets;
                    if (panel.FrozenRowCount > 0)
                    {
                        if (_PageBuckets[bucket].Contains(0))
                            continue;
                    }
                    if (panel.ActiveRow != null)
                    {
                        if (_PageBuckets[bucket].Contains(panel.ActiveRow.Index))
                            continue;
                    }
                    break;
                }
            }
            _BucketHead = (bucket + 1) % MaxBuckets;
            return (NewBucketRow(bucket, index));
        }
        #endregion
        #region NewBucketRow
        private GridRow NewBucketRow(int n, int index)
        {
            int startIndex = (index / MaxRows) * MaxRows;
            if (_PageBuckets[n] != null)
                _PageBuckets[n].Reset(_Container, startIndex);
            else
                _PageBuckets[n] = new VPageBucket(_Container, startIndex);
            _LastAccessedBucket = _PageBuckets[n];
            return (_LastAccessedBucket[index]);
        }
        #endregion
        #region Clear
        ///
        /// Clear
        ///
        public void Clear()
        {
            for (int i = 0; i < MaxBuckets; i++)
            {
                if (_PageBuckets[i] != null)
                    _PageBuckets[i].Reset(null, 0);
                _PageBuckets[i] = null;
            }
            _BucketHead = 0;
            _LastAccessedBucket = null;
        }
        #endregion
        #region VPageBucket
        private class VPageBucket
        {
            #region Static data
            static private GridLayoutInfo _layoutInfo = new GridLayoutInfo(null, Rectangle.Empty);
            static private GridLayoutStateInfo _stateInfo = new GridLayoutStateInfo(null, 0);
            #endregion
            #region Private variables
            private GridContainer _Container;
            private int _StartIndex;
            private int _EndIndex;
            private GridRow[] _Rows;
            #endregion
            public VPageBucket(GridContainer container, int startIndex)
            {
                _Container = container;
                _StartIndex = startIndex;
                _EndIndex = startIndex + MaxRows - 1;
                _Rows = new GridRow[MaxRows];
            }
            #region Public properties
            #region EndIndex
            public int EndIndex
            {
                get { return (_EndIndex); }
            }
            #endregion
            #region Row Indexer
            ///
            /// Row indexer
            ///
            ///
            public GridRow this[int index]
            {
                get { return (GetRow(index)); }
            }
            #endregion
            #region Rows
            public GridRow[] Rows
            {
                get { return (_Rows); }
            }
            #endregion
            #region StartIndex
            public int StartIndex
            {
                get { return (_StartIndex); }
            }
            #endregion
            #endregion
            #region Contains
            public bool Contains(int index)
            {
                return (index >= _StartIndex && index <= _EndIndex);
            }
            #endregion
            #region IsCached
            public bool IsCached(int index)
            {
                if (Contains(index) == true)
                    return (_Rows[index - _StartIndex] != null);
                return (false);
            }
            #endregion
            #region GetRow
            private GridRow GetRow(int index)
            {
                int n = index - _StartIndex;
                if (_Rows[n] == null)
                    _Rows[n] = AllocNewGridRow(index);
                GridRow row = _Rows[n];
                if (row.ArrangeLayoutCount != row.SuperGrid.ArrangeLayoutCount)
                    ArrangeRow(row.GridPanel, row);
                return (row);
            }
            #endregion
            #region Reset
            public void Reset(GridContainer container, int startIndex)
            {
                for (int i = 0; i < MaxRows; i++)
                {
                    GridRow row = _Rows[i];
                    if (row != null)
                    {
                        row.Dispose();
                        _Rows[i] = null;
                    }
                }
                _Container = container;
                _StartIndex = startIndex;
                _EndIndex = startIndex + MaxRows - 1;
            }
            #endregion
            #region AllocNewGridRow
            private GridRow AllocNewGridRow(int index)
            {
                GridPanel panel = _Container.GridPanel;
                if (panel != null)
                {
                    GridRow row = new GridRow();
                    row.Parent = _Container;
                    row.GridIndex = index;
                    row.Index = index;
                    row.RowIndex = index;
                    row.FullIndex = index;
                    row.MergeUpdateCount = _Container.MergeUpdateCount;
                    row.MergeUpdateCount--;
                    row.Loading = true;
                    try
                    {
                        GridColumnCollection columns = panel.Columns;
                        if (columns.Count > 0)
                        {
                            for (int i = 0; i < columns.Count; i++)
                            {
                                GridColumn col = panel.Columns[i];
                                GridCell cell = new GridCell(col.DefaultNewRowCellValue);
                                cell.Parent = row;
                                cell.ColumnIndex = i;
                                row.Cells.Add(cell);
                            }
                        }
                        ArrangeRow(panel, row);
                        if (columns.Count > 0)
                        {
                            _Container.SuperGrid.DoLoadVirtualRowEvent(panel, row);
                            _Container.SuperGrid.DoVirtualRowLoadedEvent(panel, row);
                        }
                    }
                    finally
                    {
                        row.Loading = false;
                    }
                    return (row);
                }
                return (null);
            }
            #region ArrangeRow
            private void ArrangeRow(GridPanel panel, GridRow row)
            {
                Rectangle r = panel.BoundsRelative;
                int vrh = Dpi.Height(panel.VirtualRowHeight);
                r.Y += (panel.FixedRowHeight - (panel.FrozenRowCount * vrh));
                r.Y += (row.Index * vrh);
                r.Size = new Size(panel.ColumnHeader.Size.Width, vrh);
                _layoutInfo.ClientBounds = r;
                _stateInfo.GridPanel = panel;
                row.FixedRowHeight = r.Size.Height;
                row.Arrange(_layoutInfo, _stateInfo, r);
            }
            #endregion
            #endregion
        }
        #endregion
    }
}