package cs211.examples;

import java.util.Iterator;
import java.util.ListIterator;

public class LinkedList<E> implements List<E> {
	private Node _head;
	private Node _tail;
	private int _size;
	
	
	/**
	 * Appends the specified element to the end of the list.
	 * 
	 * @param item the element to be added to the list
	 */
	@Override
	public void add(E item) {
		Node newNode = new Node(item, null);
		if (_tail != null){
			_tail.next = newNode;
		}else{
			_head = newNode;	
		}
		
		_tail = newNode;
		_size++;
		
	}

	/**
	 * Appends the specified element at the specified position.
	 * 
	 * This does not replace any existing elements at this location. 
	 * This shifts any current element and all subsequent elements 
	 * one position to the right.
	 * 
	 * @param index index at which the specified element is to be inserted
	 * @param item the element to be inserted
	 * @throws IndexOutOfBoundsException if the index is out of range (index <0 || index >size())
	 */
	@Override
	public void add(int index, E item) {
		// TODO Auto-generated method stub
		
	}

	/**
	 * Empty all items from the list.
	 */
	@Override
	public void clear() {
		Node tmp = _head;
		
		while (_head != null){
			_head = tmp.next;
			tmp.next = null;
			tmp = _head;
		}
		
		_size = 0;
		_tail = null;
		
	}

	
	/**
	 * Returns {@code true} if this list contains the specified element.
	 * 
	 * More formally, returns {@code true} if and only if this list contains
	 * at least one element {@code e} such that {@code (item == null) ? e == null : item.equals(e)}.
	 * 
	 * @param item element whose presence in this list is being tested
	 * @return {@code true} if the list contains the specified item
	 */
	@Override
	public boolean contains(E item) {
		Node tmp = _head;
		while(tmp != null){
			if (item == null && tmp.data == null){
				return true;
			}else if (item.equals(tmp.data)){
				return true;
			}
			tmp = tmp.next;
		}
		return false;
	}

	
	/**
	 * Returns the element at the specified position in this list
	 * 
	 * @param index index of the element to return
	 * @return the element at the specified position
	 * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= size())
	 */
	@Override
	public E get(int index) {
		Node tmp = _head;
		int current = 0;
		while( !(index < 0 || index >= size()) && tmp != null){
			if (current == index){
				return tmp.data;
			}
			tmp = tmp.next;
			index++;
		}
		throw new IndexOutOfBoundsException();
	}

	
	/**
	 * Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element.
	 * 
	 * More formally, returns the lowest index {@code i} such that {@code (o==null ? get(i)==null : o.equals(get(i)))},
	 *  or -1 if there is no such index.
	 *  
	 * @param item the element to search for
	 * @return the index of the first occurrence of the specified element in this list, or -1 if the list does not contain the element.
	 */
	@Override
	public int indexOf(E item) {
		Node tmp = _head;
		int index = 0;
		while(tmp != null){
			if (item == null && tmp.data == null){
				return index;
			}else if (item.equals(tmp.data)){
				return index;
			}
			tmp = tmp.next;
			index++;
		}
		return -1;
	}

	
	/**
	 * Removes the element at the specified position in this list.
	 *  
	 * Shifts any subsequent elements to the left (subtracts one from their indices). 
	 * Returns the element that was removed from the list.
	 * 
	 * @param index the index of the element to be removed
	 * @return the element previously at the specified location
	 * @throws IndexOutOfBoundsException if the index is out of range {@code (index < 0 || index >= size())}
	 */
	@Override
	public E remove(int index) {
		// TODO Auto-generated method stub
		return null;
	}

	
	/**
	 * Replaces the element at the specified position with the specified element.
	 * 
	 * @param index index of the element to replace
	 * @param item element to be stored at the specified position
	 * @throws IndexOutOfBoundsException if the index is out of the range {@code (index < 0 || index >= size())
	 */
	@Override
	public void set(int i, E item) {
		// TODO Auto-generated method stub
		
	}

	
	/**
	 * Returns the number of elements in this list.
	 * 
	 * @return the number of elements in the list
	 */
	@Override
	public int size() {
		
		return _size;
	}

	
	/**
	 * Returns an iterator over the elements in this list in proper sequence.
	 * 
	 * @return an iterator over the elements in this list in proper sequence
	 */
	@Override
	public Iterator<E> iterator() {
		return listIterator();
	}

	
	/**
	 * Returns a list iterator over the elements in this list in proper sequence.
	 * 
	 * @return a list iterator over the elements in this list in proper sequence
	 */
	@Override
	public ListIterator<E> listIterator() {
		// TODO Auto-generated method stub
		return null;
	}

	private class Node{
		private E data;
		private Node next;
		
		private Node(E data, Node next){
			this.data = data;
			this.next = next;
		}
	}
	
	
}