"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _uuid = require("uuid");

/*
 * Copyright 2020 IBM Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
class CircularBuffer {
  constructor(first, capacity = 15) {
    this.entries = new Array(capacity);
    this.activeIdx = 0;
    this.prev = -1;
    this.next = -1;
    this.insertionIdx = 1 % capacity;
    this._length = 1;
    this.entries[0] = this.entry(first, {
      prev: this.prev,
      next: this.next
    });
  }

  entry(asGiven, updatePointer) {
    return Object.assign(asGiven, {
      key: (0, _uuid.v4)(),
      prev: updatePointer.prev,
      next: updatePointer.next
    });
  }

  get length() {
    return this._length;
  }

  get key() {
    return this.peek().key;
  }

  findIndex(predicate) {
    return this.entries.findIndex(predicate);
  }

  update(idx, t) {
    if (idx !== this.activeIdx) {
      this.next = -1;
      this.prev = this.activeIdx;
      this.entries[idx] = this.entry(t, {
        prev: this.prev,
        next: this.next
      });
      this.entries[this.prev].next = idx;
    } else {
      this.entries[idx] = this.entry(t, {
        prev: this.entries[idx].prev,
        next: this.entries[idx].next
      });
    }

    this.activeIdx = idx;
  }
  /** update at this.activeIdx */


  updateActive(t) {
    this.update(this.activeIdx, t);
  }

  push(entry) {
    this.next = -1;
    this.prev = this.activeIdx;
    const idx = this.insertionIdx;
    this.entries[idx] = this.entry(entry, {
      prev: this.prev,
      next: this.next
    });
    this.entries[this.prev].next = idx;
    this.activeIdx = idx;
    this.insertionIdx = (idx + 1) % this.entries.length;
    this._length = Math.min(this._length + 1, this.entries.length);
  }
  /** pop the entry at idx */


  popAt(idx) {
    while (idx < this._length - 1) {
      const nextEntry = this.entries[idx + 1];
      this.entries[idx] = this.entry(nextEntry, {
        prev: nextEntry.prev,
        next: nextEntry.next
      });
      idx += 1;
    }

    delete this.entries[idx];
    this.activeIdx = idx - 1;
    this.insertionIdx = idx % this.entries.length;
    this._length = this._length - 1;
  }

  before() {
    this.activeIdx = this.prev;
    this.next = this.entries[this.prev].next;
    this.prev = this.entries[this.prev].prev;
    return this.peekAt(this.activeIdx);
  }

  hasBefore() {
    return this.prev >= 0;
  }

  hasAfter() {
    return this.next >= 0;
  }

  hasBuffer() {
    return this.hasBefore() || this.hasAfter();
  }

  after() {
    this.activeIdx = this.next;
    this.prev = this.entries[this.next].prev;
    this.next = this.entries[this.next].next;
    return this.peekAt(this.activeIdx);
  }

  hasLeft() {
    return this.activeIdx > 0;
  }

  peek() {
    return this.peekAt(this.activeIdx);
  }

  peekAt(idx) {
    return this.entries[idx];
  }

}

exports.default = CircularBuffer;