class LinkedList {
  constructor() {
     this.head = null;
    this.tail = null;
    this.listSize = 0;
  }

  get size() {
    return this.listSize;
  }

  isLinked(node) {
    return !((node && node.prev === null && node.next === null && this.tail !== node && this.head !== node) || this.isEmpty());
  }

  isEmpty() {
    return this.listSize === 0;
  }

  get first() {
    return this.head;
  }

  get last() {
    return this.last;
  }


  toString() {
    return this.toArray().toString();
  }

  toArray() {
    var node = this.head,
    result = [];
    while (node !== null) {
        result.push(node);
        node = node.next;
    }
    return result;
  }

  // Note that modifying the list during
  // iteration is not safe.
  forEach(fun) {
    var node = this.head;
    while (node !== null) {
        fun(node);
        node = node.next;
    }
  }

  contains(n) {
    var node = this.head;
    if (!this.isLinked(n)) {
        return false;
    }
    while (node !== null) {
        if (node === n) {
            return true;
        }
        node = node.next;
    }
    return false;
  }

  at(i) {
    var node = this.head, index = 0;

    if (i >= this.listLength || i < 0) {
        return null;
    }

    while (node !== null) {
        if (i === index) {
            return node;
        }
        node = node.next;
        index += 1;
    }
    return null;
  }

  insertAfter(node, newNode) {
    if (!this.isLinked(node)) {
        return this;
    }
    newNode.prev = node;
    newNode.next = node.next;
    if (node.next === null) {
        this.tail = newNode;
    } else {
        node.next.prev = newNode;
    }
    node.next = newNode;
    this.listSize += 1;
    return this;
  }

  insertBefore(node, newNode) {
    if (!this.isLinked(node)) {
        return this;
    }
    newNode.prev = node.prev;
    newNode.next = node;
    if (node.prev === null) {
        this.head = newNode;
    } else {
        node.prev.next = newNode;
    }
    node.prev = newNode;
    this.listSize += 1;
    return this;
  }

  push(node) {
    if (this.head === null) {
        this.unshift(node);
    } else {
        this.insertAfter(this.tail, node);
    }
    return this;
  }

  unshift(node) {
    if (this.head === null) {
        this.head = node;
        this.tail = node;
        node.prev = null;
        node.next = null;
        this.listSize += 1;
    } else {
        this.insertBefore(this.head, node);
    }
    return this;
  }

  remove(node) {
    if (!this.isLinked(node)) {
        return this;
    }
    if (node.prev === null) {
        this.head = node.next;
    } else {
        node.prev.next = node.next;
    }
    if (node.next === null) {
        this.tail = node.prev;
    } else {
        node.next.prev = node.prev;
    }
    this.listSize -= 1;
    return this;
  }

  pop() {
    var node = this.tail;
    this.tail.prev.next = null;
    this.tail = this.tail.prev;
    this.listSize -= 1;
    node.prev = null;
    node.next = null;
    return node;
  }

  shift() {
    var node = this.head;
    this.head.next.prev = null;
    this.head = this.head.next;
    this.listSize -= 1;
    node.prev = null;
    node.next = null;
    return node;
  }
}

class Node {
  constructor(data) {
    this.prev = null;
    this.next = null;
    this.data = data;
  }

  toString() {
    return this.data.toString();
  }
}
