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

  getHead() {
    return this.head;
  }
  getTail() {
    return this.tail;
  }

  getSize() {
    return this.size;
  }

  append(value) {
    let newNode = new Node(value);
    if (this.tail == null) {
      this.tail = newNode;
    } else {
      const temp = this.tail;
      this.tail = newNode;
      this.tail.nextNode = temp;
    }
    if (this.head == null) {
      this.head = newNode;
    }

    this.size += 1;
  }

  prepend(value) {
    let newNode = new Node(value);
    if (this.head == null) {
      this.head = newNode;
      if (this.tail == null) {
        this.tail = newNode;
      }
    } else {
      const temp = this.head;
      temp.nextNode = newNode;
      this.head = newNode;
    }
    this.size += 1;
  }

  at(index) {
    function nodeAtIndex(index, count, node) {
      if (index === count) {
        return node;
      } else if (node.nextNode == null) {
        return null;
      } else {
        return nodeAtIndex(index, count + 1, node.nextNode);
      }
    }

    return nodeAtIndex(index, 0, this.tail);
  }
  pop() {
    if (this.tail.nextNode == null) {
      this.tail = null;
      this.head = null;
    } else {
      this.tail = this.tail.nextNode;
    }
    this.size -= 1;
  }

  contains(value) {
    function searchNodes(node, value) {
      if (node.value === value) {
        return true;
      } else if (node.nextNode == null) {
        return false;
      } else {
        return searchNodes(node.nextNode, value);
      }
    }

    return searchNodes(this.tail, value);
  }

  find(value) {
    let index = 0;
    function searchNodes(node, value, index) {
      if (node.value === value) {
        return index;
      } else if (node.nextNode == null) {
        return null;
      } else {
        return searchNodes(node.nextNode, value, index + 1);
      }
    }

    return searchNodes(this.tail, value, index);
  }

  insertAt(value, index) {
    let previousNode = null;
    let newNode = new Node(value);
    let currentNode = this.tail;
    let count = 0;

    if (index === 0) {
      this.append(value);
      return;
    }

    while (currentNode != null) {
      if (count == index) {
        if (currentNode.nextNode == null) {
          this.prepend(value);
          return;
        } else {
          let nextNode = currentNode;
          previousNode.nextNode = newNode;
          newNode.nextNode = nextNode;
          return;
        }
      }

      previousNode = currentNode;
      currentNode = currentNode.nextNode;
      count += 1;
    }
  }

  removeAt(index) {
    let previousNode = null;
    let currentNode = this.tail;
    let count = 0;

    if (index === 0) {
      this.pop();
      return;
    }

    while (currentNode != null) {
      if (count == index) {
        if (currentNode.nextNode == null) {
          previousNode.nextNode = null;
          return;
        } else {
          previousNode.nextNode = currentNode.nextNode;
          return;
        }
      }

      previousNode = currentNode;
      currentNode = currentNode.nextNode;
      count += 1;
    }
  }

  toString() {
    let nodes = [];
    let current = this.tail;

    while (current != null) {
      nodes.push(current.value);
      current = current.nextNode;
    }

    console.log(nodes.join(" -> ") + " -> null");
  }
}

class Node {
  constructor(value) {
    this.value = value;
    this.nextNode = null;
  }
}

let list = new LinkedList();

list.append("test 5");
list.prepend("test 8");
list.append("test 1");
list.append("test 2");
list.append("test 3");
list.append("test 4");
list.prepend("Deez Nuts");
list.toString();
console.log(list.find("Deez Nuts"));
console.log(list.find("test "));
console.log(list.contains("test 3"));

console.log(list.size);
console.log(list.at(1).value);
console.log("popping last element");
list.pop();
list.toString();
list.insertAt("inserted 3", 3);
// list.insertAt("inserted 1", 1);
list.toString();
list.insertAt("inserted 1", 1);
list.toString();
list.removeAt(6);
console.log("Removing element 6");
list.toString();
list.removeAt(1);
list.removeAt(3);
list.removeAt(0);
list.removeAt(3);
console.log("Removing element 1");
list.toString();
