| /******************************************************************************* | 
 |  * You may amend and distribute as you like, but don't remove this header! | 
 |  * | 
 |  * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets. | 
 |  * See http://www.codeplex.com/EPPlus for details. | 
 |  * | 
 |  * Copyright (C) 2011  Jan Källman | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | 
 |  * See the GNU Lesser General Public License for more details. | 
 |  * | 
 |  * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php | 
 |  * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html | 
 |  * | 
 |  * All code and executables are provided "as is" with no warranty either express or implied. | 
 |  * The author accepts no liability for any damage or loss of business that this product may cause. | 
 |  * | 
 |  * Code change notes: | 
 |  * | 
 |  * Author							Change						Date | 
 |  * ****************************************************************************** | 
 |  * Jan Källman		    Added       		        2010-02-04 | 
 |  * Jan Källman		    License changed GPL-->LGPL  2011-12-27 | 
 |  *******************************************************************************/ | 
 |  | 
 | using System; | 
 | using System.Collections; | 
 | using System.Collections.Generic; | 
 |  | 
 | namespace AppsheetEpplus; | 
 |  | 
 | /// <summary> | 
 | /// This is the store for all Rows, Columns and Cells. | 
 | /// It is a Dictionary implementation that allows you to change the Key (the RowID, ColumnID or CellID ) | 
 | /// </summary> | 
 | internal class RangeCollection : IEnumerator<IRangeId>, IEnumerable { | 
 |   private class IndexItem { | 
 |     internal IndexItem(ulong cellId) { | 
 |       RangeID = cellId; | 
 |     } | 
 |  | 
 |     internal IndexItem(ulong cellId, int listPointer) { | 
 |       RangeID = cellId; | 
 |       ListPointer = listPointer; | 
 |     } | 
 |  | 
 |     internal ulong RangeID; | 
 |     internal int ListPointer; | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Compares an IndexItem | 
 |   /// </summary> | 
 |   internal class Compare : IComparer<IndexItem> { | 
 |     int IComparer<IndexItem>.Compare(IndexItem x, IndexItem y) { | 
 |       return x.RangeID < y.RangeID | 
 |           ? -1 | 
 |           : x.RangeID > y.RangeID | 
 |               ? 1 | 
 |               : 0; | 
 |     } | 
 |   } | 
 |  | 
 |   private IndexItem[] _cellIndex; | 
 |   private List<IRangeId> _cells; | 
 |   private static readonly Compare _comparer = new(); | 
 |  | 
 |   /// <summary> | 
 |   /// Creates a new collection | 
 |   /// </summary> | 
 |   /// <param name="cells">The Cells. This list must be sorted</param> | 
 |   internal RangeCollection(List<IRangeId> cells) { | 
 |     _cells = cells; | 
 |     InitSize(_cells); | 
 |     for (int i = 0; i < _cells.Count; i++) { | 
 |       _cellIndex[i] = new(cells[i].RangeID, i); | 
 |     } | 
 |   } | 
 |  | 
 |   ~RangeCollection() { | 
 |     _cells = null; | 
 |     _cellIndex = null; | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Return the item with the RangeID | 
 |   /// </summary> | 
 |   /// <param name="rangeId"></param> | 
 |   /// <returns></returns> | 
 |   internal IRangeId this[ulong rangeId] => _cells[_cellIndex[IndexOf(rangeId)].ListPointer]; | 
 |  | 
 |   /// <summary> | 
 |   /// Return specified index from the sorted list | 
 |   /// </summary> | 
 |   /// <param name="index"></param> | 
 |   /// <returns></returns> | 
 |   internal IRangeId this[int index] => _cells[_cellIndex[index].ListPointer]; | 
 |  | 
 |   internal int Count => _cells.Count; | 
 |  | 
 |   internal void Add(IRangeId cell) { | 
 |     var ix = IndexOf(cell.RangeID); | 
 |     if (ix >= 0) { | 
 |       throw (new("Item already exists")); | 
 |     } | 
 |     Insert(~ix, cell); | 
 |   } | 
 |  | 
 |   internal void Delete(ulong key) { | 
 |     var ix = IndexOf(key); | 
 |     if (ix < 0) { | 
 |       throw (new("Key does not exists")); | 
 |     } | 
 |     int listPointer = _cellIndex[ix].ListPointer; | 
 |     Array.Copy(_cellIndex, ix + 1, _cellIndex, ix, _cells.Count - ix - 1); | 
 |     _cells.RemoveAt(listPointer); | 
 |  | 
 |     //Item is removed subtract one from all items with greater ListPointer | 
 |     for (int i = 0; i < _cells.Count; i++) { | 
 |       if (_cellIndex[i].ListPointer >= listPointer) { | 
 |         _cellIndex[i].ListPointer--; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   internal int IndexOf(ulong key) { | 
 |     return Array.BinarySearch(_cellIndex, 0, _cells.Count, new(key), _comparer); | 
 |   } | 
 |  | 
 |   internal bool ContainsKey(ulong key) { | 
 |     return IndexOf(key) < 0 ? false : true; | 
 |   } | 
 |  | 
 |   private int _size { get; set; } | 
 |  | 
 |   /// <summary> | 
 |   /// Insert a number of rows in the collecion but dont update the cell only the index | 
 |   /// </summary> | 
 |   /// <param name="rowId"></param> | 
 |   /// <param name="rows"></param> | 
 |   /// <returns>Index of first rangeItem</returns> | 
 |   internal int InsertRowsUpdateIndex(ulong rowId, int rows) { | 
 |     int index = IndexOf(rowId); | 
 |     if (index < 0) { | 
 |       index = ~index; //No match found invert to get start cell | 
 |     } | 
 |     ulong rowAdd = (((ulong)rows) << 29); | 
 |     for (int i = index; i < _cells.Count; i++) { | 
 |       _cellIndex[i].RangeID += rowAdd; | 
 |     } | 
 |     return index; | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Insert a number of rows in the collecion | 
 |   /// </summary> | 
 |   /// <param name="rowId"></param> | 
 |   /// <param name="rows"></param> | 
 |   /// <returns>Index of first rangeItem</returns> | 
 |   internal int InsertRows(ulong rowId, int rows) { | 
 |     int index = IndexOf(rowId); | 
 |     if (index < 0) { | 
 |       index = ~index; //No match found invert to get start cell | 
 |     } | 
 |     ulong rowAdd = (((ulong)rows) << 29); | 
 |     for (int i = index; i < _cells.Count; i++) { | 
 |       _cellIndex[i].RangeID += rowAdd; | 
 |       _cells[_cellIndex[i].ListPointer].RangeID += rowAdd; | 
 |     } | 
 |     return index; | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Delete rows from the collecion | 
 |   /// </summary> | 
 |   /// <param name="rowId"></param> | 
 |   /// <param name="rows"></param> | 
 |   /// <param name="updateCells">Update range id's on cells</param> | 
 |   internal int DeleteRows(ulong rowId, int rows, bool updateCells) { | 
 |     ulong rowAdd = (((ulong)rows) << 29); | 
 |     var index = IndexOf(rowId); | 
 |     if (index < 0) { | 
 |       index = ~index; //No match found invert to get start cell | 
 |     } | 
 |  | 
 |     if (index >= _cells.Count || _cellIndex[index] == null) { | 
 |       return -1; //No row above this row | 
 |     } | 
 |     while (index < _cells.Count && _cellIndex[index].RangeID < rowId + rowAdd) { | 
 |       Delete(_cellIndex[index].RangeID); | 
 |     } | 
 |  | 
 |     int updIndex = IndexOf(rowId + rowAdd); | 
 |     if (updIndex < 0) { | 
 |       updIndex = ~updIndex; //No match found invert to get start cell | 
 |     } | 
 |  | 
 |     for (int i = updIndex; i < _cells.Count; i++) { | 
 |       _cellIndex[i].RangeID -= rowAdd; //Change the index | 
 |       if (updateCells) { | 
 |         _cells[_cellIndex[i].ListPointer].RangeID -= rowAdd; //Change the cell/row or column object | 
 |       } | 
 |     } | 
 |     return index; | 
 |   } | 
 |  | 
 |   internal void InsertColumn(ulong columnId, int columns) { | 
 |     throw (new("Working on it...")); | 
 |   } | 
 |  | 
 |   internal void DeleteColumn(ulong columnId, int columns) { | 
 |     throw (new("Working on it...")); | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Init the size starting from 128 items. Double the size until the list fits. | 
 |   /// </summary> | 
 |   /// <param name="_cells"></param> | 
 |   private void InitSize(List<IRangeId> cells) { | 
 |     _size = 128; | 
 |     while (cells.Count > _size) { | 
 |       _size <<= 1; | 
 |     } | 
 |     _cellIndex = new IndexItem[_size]; | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Check the size and double the size if out of bound | 
 |   /// </summary> | 
 |   private void CheckSize() { | 
 |     if (_cells.Count >= _size) { | 
 |       _size <<= 1; | 
 |       Array.Resize(ref _cellIndex, _size); | 
 |     } | 
 |   } | 
 |  | 
 |   private void Insert(int ix, IRangeId cell) { | 
 |     CheckSize(); | 
 |     Array.Copy(_cellIndex, ix, _cellIndex, ix + 1, _cells.Count - ix); | 
 |     _cellIndex[ix] = new(cell.RangeID, _cells.Count); | 
 |     _cells.Add(cell); | 
 |   } | 
 |  | 
 |   IRangeId IEnumerator<IRangeId>.Current { | 
 |     get { throw new NotImplementedException(); } | 
 |   } | 
 |  | 
 |   void IDisposable.Dispose() { | 
 |     _ix = -1; | 
 |   } | 
 |  | 
 |   private int _ix = -1; | 
 |  | 
 |   object IEnumerator.Current { | 
 |     get { return _cells[_cellIndex[_ix].ListPointer]; } | 
 |   } | 
 |  | 
 |   bool IEnumerator.MoveNext() { | 
 |     _ix++; | 
 |     return _ix < _cells.Count; | 
 |   } | 
 |  | 
 |   void IEnumerator.Reset() { | 
 |     _ix = -1; | 
 |   } | 
 |  | 
 |   IEnumerator IEnumerable.GetEnumerator() { | 
 |     return MemberwiseClone() as IEnumerator; | 
 |   } | 
 | } |