using System;
using System.Collections.Generic;
namespace DevComponents.DotNetBar.SuperGrid
{
    ///
    /// SelectedElements
    ///
    public class SelectedElements
    {
        #region Private variables
        private List _Ranges;
        private SelectedElementType _ElementType;
        #endregion
        ///
        /// SelectedElements
        ///
        public SelectedElements()
        {
            _Ranges = new List();
        }
        ///
        /// SelectedElements
        ///
        public SelectedElements(SelectedElementType elementType)
            : this()
        {
            ElementType = elementType;
        }
        #region Public properties
        #region Count
        ///
        /// Count
        ///
        public int Count
        {
            get
            {
                int count = 0;
                if (_Ranges != null)
                {
                    foreach (SelectedRange range in _Ranges)
                        count += range.Count;
                }
                return (count);
            }
        }
        #endregion
        #region ElementType
        /// 
        /// Element Type
        /// 
        public SelectedElementType ElementType
        {
            get { return (_ElementType); }
            set { _ElementType = value; }
        }
        #endregion
        #region FirstIndex
        ///
        /// FirstIndex
        ///
        public int FirstIndex
        {
            get
            {
                if (_Ranges.Count > 0)
                    return (_Ranges[0].StartIndex);
                return (-1);
            }
        }
        #endregion
        #region LastIndex
        ///
        /// LastIndex
        ///
        public int LastIndex
        {
            get
            {
                if (_Ranges.Count > 0)
                    return (_Ranges[_Ranges.Count - 1].EndIndex);
                return (-1);
            }
        }
        #endregion
        #region Ranges
        ///
        /// Items
        ///
        public List Ranges
        {
            get { return (_Ranges); }
        }
        #endregion
        #endregion
        #region FindRange
        ///
        /// FindRange
        ///
        ///
        ///
        ///
        public bool FindRange(int index, out SelectedRange selRange)
        {
            foreach (SelectedRange range in _Ranges)
            {
                if (range.Contains(index) == true)
                {
                    selRange = range;
                    return (true);
                }
            }
            selRange = null;
            return (false);
        }
        #endregion
        #region AddItem
        ///
        /// AddItem
        ///
        ///
        ///
        public void AddItem(int index)
        {
            for (int i = 0; i < _Ranges.Count; i++)
            {
                SelectedRange range = _Ranges[i];
                if (index <= range.EndIndex + 1)
                {
                    AddItem(range, i, index);
                    return;
                }
            }
            _Ranges.Add(new SelectedRange(index, index));
            ValidateRanges();
        }
        private void AddItem(SelectedRange range, int i, int index)
        {
            if (range.StartIndex - 1 == index)
            {
                range.StartIndex--;
            }
            else if (range.EndIndex + 1 == index)
            {
                range.EndIndex++;
                if (i + 1 < _Ranges.Count)
                {
                    if (_Ranges[i + 1].StartIndex == index + 1)
                    {
                        _Ranges[i].EndIndex = _Ranges[i + 1].EndIndex;
                        _Ranges.RemoveAt(i + 1);
                    }
                }
            }
            else if (range.Contains(index) == false)
            {
                SelectedRange selRange = new SelectedRange(index, index);
                _Ranges.Insert(i, selRange);
            }
            ValidateRanges();
        }
        #endregion
        #region AddRange
        ///
        ///
        ///
        ///
        public void AddRange(int startIndex, int endIndex)
        {
            for (int i = 0; i < _Ranges.Count; i++)
            {
                SelectedRange range = _Ranges[i];
                if (endIndex == range.StartIndex - 1)
                {
                    range.StartIndex = startIndex;
                    if (i > 0)
                    {
                        if (_Ranges[i - 1].EndIndex + 1 == startIndex)
                        {
                            _Ranges[i - 1].EndIndex = range.EndIndex;
                            _Ranges.RemoveAt(i);
                        }
                    }
                    CoalesceRanges();
                    ValidateRanges();
                    return;
                }
                if (startIndex == range.EndIndex + 1)
                {
                    range.EndIndex = endIndex;
                    if (i + 1 < _Ranges.Count)
                    {
                        if (_Ranges[i + 1].StartIndex - 1 == endIndex)
                        {
                            range.EndIndex = _Ranges[i + 1].EndIndex;
                            _Ranges.RemoveAt(i + 1);
                        }
                    }
                    CoalesceRanges();
                    ValidateRanges();
                    return;
                }
            }
            SelectedRange selRange = null;
            for (int i = 0; i < _Ranges.Count; i++)
            {
                SelectedRange range = _Ranges[i];
                if (range.StartIndex > startIndex)
                {
                    selRange = new SelectedRange(startIndex, endIndex);
                    _Ranges.Insert(i, selRange);
                    break;
                }
            }
            if (selRange == null)
            {
                selRange = new SelectedRange(startIndex, endIndex);
                _Ranges.Add(selRange);
            }
            CoalesceRanges();
        }
        #region CoalesceRanges
        private void CoalesceRanges()
        {
            SelectedRange xRange = _Ranges[0];
            for (int i = 1; i < _Ranges.Count; i++)
            {
                SelectedRange range = _Ranges[i];
                if (range.StartIndex - 1 <= xRange.EndIndex)
                {
                    if (range.EndIndex > xRange.EndIndex)
                        xRange.EndIndex = range.EndIndex;
                    if (range.StartIndex < xRange.StartIndex)
                        xRange.StartIndex = range.StartIndex;
                    range.StartIndex = -1;
                }
                else
                {
                    xRange = range;
                }
            }
            for (int i = _Ranges.Count - 1; i > 0; i--)
            {
                SelectedRange range = _Ranges[i];
                if (range.StartIndex == -1)
                    _Ranges.RemoveAt(i);
            }
            ValidateRanges();
        }
        #endregion
        #endregion
        #region RemoveItem
        ///
        /// RemoveItem
        ///
        ///
        public void RemoveItem(int index)
        {
            SelectedRange selRange;
            if (FindRange(index, out selRange) == true)
                RemoveItem(index, selRange);
        }
        ///
        /// RemoveItem
        ///
        ///
        ///
        ///
        public void RemoveItem(int index, SelectedRange range)
        {
            if (range.StartIndex == index)
            {
                range.StartIndex++;
                if (range.StartIndex > range.EndIndex)
                    _Ranges.Remove(range);
            }
            else if (range.EndIndex == index)
            {
                range.EndIndex--;
                if (range.StartIndex > range.EndIndex)
                    _Ranges.Remove(range);
            }
            else if (range.Contains(index) == true)
            {
                int i = _Ranges.IndexOf(range);
                int endIndex = range.EndIndex;
                range.EndIndex = index - 1;
                _Ranges.Insert(i + 1,
                    new SelectedRange(index + 1, endIndex));
            }
            ValidateRanges();
        }
        #endregion
        #region RemoveRange
        ///
        ///
        ///
        ///
        public bool RemoveRange(int startIndex, int endIndex)
        {
            bool itemsRemoved = false;
            for (int i = 0; i < _Ranges.Count; i++)
            {
                SelectedRange range = _Ranges[i];
                if (range.EndIndex < startIndex)
                    continue;
                if (range.StartIndex > endIndex)
                    break;
                if (range.EndIndex >= startIndex && range.EndIndex <= endIndex)
                {
                    if (range.StartIndex < startIndex)
                        range.EndIndex = startIndex - 1;
                    else
                        range.StartIndex = -1;
                    itemsRemoved = true;
                }
                else if (range.StartIndex >= startIndex && range.StartIndex <= endIndex)
                {
                    if (range.EndIndex > endIndex)
                        range.StartIndex = endIndex + 1;
                    else
                        range.StartIndex = -1;
                    itemsRemoved = true;
                }
                else
                {
                    int index = range.EndIndex;
                    range.EndIndex = startIndex - 1;
                    _Ranges.Insert(i + 1,
                        new SelectedRange(endIndex + 1, index));
                    itemsRemoved = true;
                    break;
                }
            }
            for (int i = _Ranges.Count - 1; i >= 0; i--)
            {
                SelectedRange range = _Ranges[i];
                if (range.StartIndex == -1)
                    _Ranges.RemoveAt(i);
            }
            ValidateRanges();
            return (itemsRemoved);
        }
        private void ValidateRanges()
        {
            int n = -2;
            for (int i = 0; i < _Ranges.Count; i++)
            {
                SelectedRange range = _Ranges[i];
                if (range.StartIndex - 1 <= n)
                    throw new Exception("Invalid Range StartIndex!");
                if (range.StartIndex > range.EndIndex)
                    throw new Exception("Invalid Range EndIndex!");
                n = range.EndIndex;
            }
        }
        #endregion
        #region Clear
        ///
        /// Clear
        ///
        public void Clear()
        {
            _Ranges.Clear();
        }
        #endregion
        #region OffsetIndices
        ///
        /// OffsetIndices
        ///
        ///
        ///
        public void OffsetIndices(int index, int count)
        {
            for (int i=0; i<_Ranges.Count; i++)
            {
                SelectedRange range = _Ranges[i];
                if (range.EndIndex >= index)
                {
                    if (range.StartIndex < index)
                    {
                        SelectedRange range2 = new SelectedRange(index, range.EndIndex);
                        range.EndIndex = index - 1;
                        _Ranges.Insert(++i, range2);
                    }
                                       
                    OffsetRanges(i, count);
                    CoalesceRanges();
                    break;
                }
            }
            ValidateRanges();
        }
        private void OffsetRanges(int n, int count)
        {
            for (int i = n; i < _Ranges.Count; i++)
            {
                SelectedRange range = _Ranges[i];
                range.StartIndex += count;
                range.EndIndex += count;
            }
        }
        #endregion
        #region GetNextIndex
        ///
        /// GetNextIndex
        ///
        ///
        ///
        public bool GetNextIndex(ref int index)
        {
            if (Count > 0)
            {
                if (index < LastIndex)
                {
                    index = (index < 0)
                        ? FirstIndex : NextIndex(index);
                    return (true);
                }
            }
            return (false);
        }
        #region NextIndex
        private int NextIndex(int index)
        {
            index++;
            foreach (SelectedRange range in _Ranges)
            {
                if (range.StartIndex >= index)
                    return (range.StartIndex);
                if (range.EndIndex >= index)
                    return (index);
            }
            return (LastIndex);
        }
        #endregion
        #endregion
        #region GetPrevIndex
        ///
        /// GetPreviousIndex
        ///
        ///
        ///
        public bool GetPrevIndex(ref int index)
        {
            if (Count > 0)
            {
                if (index > 0)
                {
                    index = (index > LastIndex)
                        ? LastIndex : PrevIndex(index);
                    return (index >= 0);
                }
            }
            return (false);
        }
        #region PrevIndex
        private int PrevIndex(int index)
        {
            index--;
            for (int i=_Ranges.Count - 1; i >= 0; --i)
            {
                SelectedRange range = _Ranges[i];
                if (range.EndIndex <= index)
                    return (range.EndIndex);
                if (range.StartIndex <= index)
                    return (index);
            }
            return (-1);
        }
        #endregion
        #endregion
        #region Copy
        internal SelectedElements Copy()
        {
            SelectedElements copy = new SelectedElements(_ElementType);
            foreach (SelectedRange range in _Ranges)
                copy.AddRange(range.StartIndex, range.EndIndex);
            return (copy);
        }
        #endregion
    }
    #region SelectedRange class
    ///
    /// SelectedRange
    ///
    public class SelectedRange
    {
        #region Private variables
        private int _StartIndex;
        private int _EndIndex;
        #endregion
        ///
        ///
        ///
        ///
        public SelectedRange(int startIndex, int endIndex)
        {
            _StartIndex = startIndex;
            _EndIndex = endIndex;
        }
        #region Public properties
        #region Count
        ///
        /// Count
        ///
        public int Count
        {
            get { return (_EndIndex - _StartIndex + 1); }
        }
        #endregion
        #region EndIndex
        ///
        /// EndIndex
        ///
        public int EndIndex
        {
            get { return (_EndIndex); }
            set { _EndIndex = value; }
        }
        #endregion
        #region StartIndex
        ///
        /// StartIndex
        ///
        public int StartIndex
        {
            get { return (_StartIndex); }
            set { _StartIndex = value; }
        }
        #endregion
        #endregion
        #region Contains
        ///
        /// Contains
        ///
        ///
        ///
        public bool Contains(int index)
        {
            return (index >= _StartIndex && index <= _EndIndex);
        }
        #endregion
    }
    #endregion
    #region enums
    public enum SelectedElementType
    {
        /// 
        /// Unknown range type
        /// 
        Unknown,
        /// 
        /// Selected Cells
        /// 
        SelectedCells,
        /// 
        /// Selected Columns
        /// 
        SelectedColumns,
         /// 
        /// Selected Rows
        /// 
        SelectedRows,
       /// 
        /// Deleted Rows
        /// 
        DeletedRows,
    }
    #endregion
}