import 'dart:collection'; class CircularList with ListMixin { CircularList(int maxLength) : _array = List.filled(maxLength, null); late List _array; var _length = 0; var _startIndex = 0; // Gets the cyclic index for the specified regular index. The cyclic index can then be used on the // backing array to get the element associated with the regular index. int _getCyclicIndex(int index) { return (_startIndex + index) % _array.length; } int get maxLength { return _array.length; } set maxLength(int value) { if (value <= 0) throw ArgumentError.value( value, 'value', 'maxLength can\'t be negative!'); if (value == _array.length) return; // Reconstruct array, starting at index 0. Only transfer values from the // indexes 0 to length. final newArray = List.generate( value, (index) => index < _array.length ? _array[_getCyclicIndex(index)] : null, ); _startIndex = 0; _array = newArray; } int get length { return _length; } set length(int value) { if (value > _length) { for (int i = length; i < value; i++) { _array[i] = null; } } _length = value; } // void forEach( // void Function(T? item, int index) callback, [ // bool includeBuffer = false, // ]) { // final len = includeBuffer ? _array.length : _length; // for (int i = 0; i < len; i++) { // callback(_array[_getCyclicIndex(i)], i); // } // } T operator [](int index) { if (index >= length) { throw RangeError.range(index, 0, length - 1); } return _array[_getCyclicIndex(index)]!; } operator []=(int index, T value) { if (index >= length) { throw RangeError.range(index, 0, length - 1); } _array[_getCyclicIndex(index)] = value; } void clear() { _startIndex = 0; _length = 0; } void push(T value) { _array[_getCyclicIndex(_length)] = value; if (_length == _array.length) { _startIndex++; if (_startIndex == _array.length) { _startIndex = 0; } } else { _length++; } } /// Removes and returns the last value on the list T pop() { return _array[_getCyclicIndex(_length-- - 1)]!; } /// Deletes item at [index]. void remove(int index, [int count = 1]) { if (count > 0) { for (var i = index; i < _length - count; i++) { _array[_getCyclicIndex(i)] = _array[_getCyclicIndex(i + count)]; } length -= count; } } /// Inserts [item] at [index]. void insert(int index, T item) { for (var i = _length - 1; i >= index; i--) { _array[_getCyclicIndex(i + 1)] = _array[_getCyclicIndex(i)]; } _array[_getCyclicIndex(index)] = item; if (_length + 1 > _array.length) { _startIndex += 1; onTrimmed?.call(1); } else { _length++; } } /// Inserts [items] at [index] in order. void insertAll(int index, List items) { for (var i = _length - 1; i >= index; i--) { _array[_getCyclicIndex(i + 1)] = _array[_getCyclicIndex(i)]; } for (var i = 0; i < items.length; i++) { _array[_getCyclicIndex(index + i)] = items[i]; } if (_length + items.length > _array.length) { final countToTrim = _length + items.length - _array.length; _startIndex += countToTrim; length = _array.length; } else { _length += items.length; } } void trimStart(int count) { if (count > _length) count = _length; _startIndex += count; _startIndex %= _array.length; _length -= count; } void shiftElements(int start, int count, int offset) { if (count < 0) return; if (start < 0 || start >= _length) throw Exception('Start argument is out of range'); if (start + offset < 0) throw Exception('Can not shift elements in list beyond index 0'); if (offset > 0) { for (var i = count - 1; i >= 0; i--) { this[start + i + offset] = this[start + i]; } var expandListBy = (start + count + offset) - _length; if (expandListBy > 0) { _length += expandListBy; while (_length > _array.length) { length--; _startIndex++; } } } else { for (var i = 0; i < count; i++) { this[start + i + offset] = this[start + i]; } } } bool get isFull => length == maxLength; }