// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:math';

class RingBuffer<T> {
  RingBuffer(this._bufferSize) {
    _buffer = List<T?>.filled(
      _bufferSize,
      null,
    );
  }

  Iterable<T> call() sync* {
    for (int i = _size - 1; i >= 0; --i) {
      yield _buffer[(_count - i - 1) % _bufferSize]!;
    }
  }

  /// Inserts a new element into the [RingBuffer].
  ///
  /// Returns the element evicted as a result of adding the new element if the
  /// buffer is as max capacity, null otherwise.
  T? add(T element) {
    if (_buffer.isEmpty) {
      return null;
    }
    T? evicted;
    final index = _count % _bufferSize;
    if (index < _count) {
      evicted = _buffer[index];
    }
    _buffer[index] = element;
    _count++;
    return evicted;
  }

  void resize(int size) {
    assert(size >= 0);
    if (size == _bufferSize) {
      return;
    }
    final resized = List<T?>.filled(
      size,
      null,
    );
    int count = 0;
    if (size > 0) {
      for (final e in this()) {
        resized[count++ % size] = e;
      }
    }
    _count = count;
    _bufferSize = size;
    _buffer = resized;
  }

  bool get isTruncated => _count % bufferSize < _count;

  int get bufferSize => _bufferSize;

  int get _size => min(_count, _bufferSize);

  int _bufferSize;
  int _count = 0;
  late List<T?> _buffer;
}
