/**
 * Mnemonist CircularBuffer
 * =========================
 *
 * Circular buffer implementation fit to use as a finite deque.
 */
var iterables = require('./utils/iterables.js'),
    FixedDeque = require('./fixed-deque');

/**
 * CircularBuffer.
 *
 * @constructor
 */
function CircularBuffer(ArrayClass, capacity) {

  if (arguments.length < 2)
    throw new Error('mnemonist/circular-buffer: expecting an Array class and a capacity.');

  if (typeof capacity !== 'number' || capacity <= 0)
    throw new Error('mnemonist/circular-buffer: `capacity` should be a positive number.');

  this.ArrayClass = ArrayClass;
  this.capacity = capacity;
  this.items = new ArrayClass(this.capacity);
  this.clear();
}

/**
 * Pasting most of the prototype from FixedDeque.
 */
function paste(name) {
  CircularBuffer.prototype[name] = FixedDeque.prototype[name];
}

Object.keys(FixedDeque.prototype).forEach(paste);

if (typeof Symbol !== 'undefined')
  Object.getOwnPropertySymbols(FixedDeque.prototype).forEach(paste);

/**
 * Method used to append a value to the buffer.
 *
 * @param  {any}    item - Item to append.
 * @return {number}      - Returns the new size of the buffer.
 */
CircularBuffer.prototype.push = function(item) {
  var index = this.start + this.size;

  if (index >= this.capacity)
    index -= this.capacity;

  this.items[index] = item;

  // Overwriting?
  if (this.size === this.capacity) {
    index++;

    // Wrapping around?
    if (index >= this.capacity) {
      this.start = 0;
    }
    else {
      this.start = index;
    }

    return this.size;
  }

  return ++this.size;
};

/**
 * Method used to prepend a value to the buffer.
 *
 * @param  {any}    item - Item to prepend.
 * @return {number}      - Returns the new size of the buffer.
 */
CircularBuffer.prototype.unshift = function(item) {
  var index = this.start - 1;

  if (this.start === 0)
    index = this.capacity - 1;

  this.items[index] = item;

  // Overwriting
  if (this.size === this.capacity) {

    this.start = index;

    return this.size;
  }

  this.start = index;

  return ++this.size;
};

/**
 * Static @.from function taking an arbitrary iterable & converting it into
 * a circular buffer.
 *
 * @param  {Iterable} iterable   - Target iterable.
 * @param  {function} ArrayClass - Array class to use.
 * @param  {number}   capacity   - Desired capacity.
 * @return {FiniteStack}
 */
CircularBuffer.from = function(iterable, ArrayClass, capacity) {
  if (arguments.length < 3) {
    capacity = iterables.guessLength(iterable);

    if (typeof capacity !== 'number')
      throw new Error('mnemonist/circular-buffer.from: could not guess iterable length. Please provide desired capacity as last argument.');
  }

  var buffer = new CircularBuffer(ArrayClass, capacity);

  if (iterables.isArrayLike(iterable)) {
    var i, l;

    for (i = 0, l = iterable.length; i < l; i++)
      buffer.items[i] = iterable[i];

    buffer.size = l;

    return buffer;
  }

  iterables.forEach(iterable, function(value) {
    buffer.push(value);
  });

  return buffer;
};

/**
 * Exporting.
 */
module.exports = CircularBuffer;
