package list.mylinkedlist;

import java.util.List;
import java.util.NoSuchElementException;

public class MyLinkedList<E> implements List<E> {
		
	/*
	 * Classe Nodo
	 */
	private static class Node<E> {
		
		// Dati del nodo
		private E data;
		
		// Riferimenti elemento precedente e successivo
		private Node<E> prev;
		private Node<E> next;
		
		// Costruttore
		public Node(E data, Node<E> prev, Node<E> next) {
			this.data = data;
			this.prev = prev;
			this.next = next;
		}
		
	}
	
	private Node<E> head = null;
	private Node<E> tail = null;
	private int size = 0;
	
	// Costruttore
	public MyLinkedList() {}
	
	// Metodi
	
	public boolean isEmpty() {
		return size == 0;
	}
	
	public int size() {
		return size;
	}
	
	public E getFirst() {
		if (head == null) throw new NullPointerException();
		return head.data;
	}
	
	public void addFirst(E item) {
		if (item == null) throw new NullPointerException();
		Node<E> newNode = new Node<E>(item, null, null);
		if (size == 0) {
			head = tail = newNode;
		} else {
			head.prev = newNode;
			newNode.next = head;
			head = newNode;
		}
		size++;
	}
	
	public E removeFirst() {
		if (head == null) throw new NoSuchElementException();
		E tmp = head.data;
		if (size == 1) {
			head = tail = null;
		} else {
			Node<E> firstPlace = head;
			Node<E> secondPlace = head.next;
			
			head = secondPlace;
			secondPlace.prev = null;
			firstPlace.next = null;
		}
		size--;
		return tmp;
	}
	
	public E getLast() {
		if (tail == null) throw new NullPointerException();
		return tail.data;
	}
	
	public E removeLast() {
		if (tail == null) throw new NoSuchElementException();
		E tmp = tail.data;
		if (size == 1) {
			head = tail = null;
		} else {
			Node<E> lastNode = tail;
			Node<E> penultimateNode = tail.prev;
			
			tail = penultimateNode;
			penultimateNode.next = null;
			lastNode.prev = null;
		}
		size--;
		return tmp;
	}
	
	public void addLast(E item) {
		if (item == null) throw new NullPointerException();
		Node<E> newNode = new Node<E>(item, null, null);
		if (size == 0) {
			head = tail = newNode;
		} else {
			Node<E> oldLastNode = tail;
			oldLastNode.next = newNode;
			newNode.prev = oldLastNode;
			tail = newNode;
		}
		size++;
	}
	
	public void clear() {
		head = tail = null;
		size = 0;
	}
	
	public boolean add(E item) {
		if (item == null) throw new NoSuchElementException();
		addLast(item);
		return true;
	}
	
	public void add(int index, E item) {
		if (item == null) throw new NullPointerException();
		if (index < 0 || index > size) throw new IndexOutOfBoundsException();
		// Se l'inserimento è richiesto nella prima posizione
		if (index == 0) {
			addFirst(item);
			return;
		}
		// Se l'inserimento è richiesto nell'ultima posizione
		if (index == size) {
			addLast(item);
			return;
		}
		// Se l'inserimento è richiesto nel generico posto i
		Node<E> newNode = new Node<E>(item, null, null);
		
		Node<E> prevNode = head;
		for (int i = 0; i < index - 1; i++) {
			prevNode = prevNode.next;
		}
		
		Node<E> nextNode = prevNode.next;
		
		prevNode.next = newNode;
		newNode.prev = prevNode;
		newNode.next = nextNode;
		nextNode.prev = newNode;
		
		size++;
	}
	
	public E get(int index) {
		if (index < 0 || index >= size) throw new IndexOutOfBoundsException();
		
		Node<E> currentNode = head;
		for (int i = 0; i < index; i++) {
			currentNode = currentNode.next;
		}
		return currentNode.data;
	}
	
	public E set(int index, E item) {
		if (index < 0 || index >= size) throw new IndexOutOfBoundsException();
		
		Node<E> currentNode = head;
		for (int i = 0; i < index; i++) {
			currentNode = currentNode.next;
		}
		E oldData = currentNode.data;
		currentNode.data = item;
		return oldData;
	}
	
	public String toString() {
		StringBuffer result = new StringBuffer();
	}
	
}
