import java.lang.NullPointerException;
/**
 * Doubly linked implementation of a list.
 * 
 */
public class LinkedList implements ListADT
{
    // need reference to at least the first and last node.
    public Node first, last;
    
    // also need to keep track of the size of the list
    public int size;
    
    public LinkedList()
    {
        first = null;
        last = null;
        size = 0;
    }
    
    public LinkedList( int startingSize )
    {
        first = null;
        last = null;
        size = 0;
    }
    
    public Object append(Object newItem) throws NullPointerException
    {
        if( newItem == null )
            throw new NullPointerException("Cannot append null object");

        Node newOne = new Node( newItem );
        if( size == 0 )
            first = newOne;
            
        else
        {
            last.next = newOne;
            newOne.previous = last;
        }
        last = newOne;
        ++size;
        
        return newOne.data;
    }
    
    public Object add(int index, Object newItem) throws NullPointerException, ListLocationException
    {
        if( newItem == null )
            throw new NullPointerException("Can't add null object");
        
        if( index < 0 || index > size )
            throw new ListLocationException("Index " + index + " is outside acceptable parameters.");
        
        Node newOne = new Node( newItem );
        
        // append
        if( index == size )
        {            
            if( size != 0 )
            {
                newOne.previous = last;
                last.next = newOne;
            }
            else
                first = newOne;
                
            last = newOne;
        }
        
        else if( index == 0 )
        {
            newOne.next = first;
            first.previous = newOne;
            first = newOne;
        }
        
        else
        {
            // add newOne after . . .
            Node thisOne;
            // start looking from
            thisOne = first;
            
            for( int i = 1; i < index; ++i )
               thisOne = thisOne.next;
               
            newOne.next = thisOne.next;
            thisOne.next = newOne;
            newOne.next.previous = newOne;
            newOne.previous = thisOne;
        }
            
        ++size;
        return newOne.data;
    }
    
    public Object remove(int index) throws ListLocationException
    {
        if( index < 0 || index >= size )
            throw new ListLocationException("Index " + index + " is outside acceptable parameters.");

        Node removedOne;
        if( index == size-1 )
        {
            removedOne = last;
            if( size > 1 )
                last = last.previous;
        }
        else if( index == 0 )
        {
            removedOne = first;
            if( size > 1 )
                first = first.next;
        }
        else if( size != 0 )
        {
            // find the one to remove
            Node theOne = first;
            for( int i = 0; i < index; ++i )
                theOne = theOne.next;
            
            removedOne = theOne;
            theOne.previous.next = theOne.next;
            theOne.next.previous = theOne.previous;
        }
        else
            removedOne = null;
        
        --size;
        
        return removedOne.data;
    }
    
    public Object replace(int index, Object newItem) throws NullPointerException, ListLocationException
    {
        if( newItem == null )
            throw new NullPointerException("Can't replace using a null object");
            
        if( index < 0 || index >= size )
            throw new ListLocationException("Index " + index + " is outside acceptable parameters.");

        Node newOne = new Node( newItem );
        Node replacedOne;
        
        if( index == size-1 )
        {
            replacedOne = last;
            newOne.previous = last.previous;
            newOne.previous.next = newOne;
        }
        else if( index == 0 )
        {
            replacedOne = first;
            newOne.next = first.next;
            newOne.next.previous = newOne;
        }
        else
        {
            // find the one to replace
            Node theOne = first;
            for( int i = 0; i < index; ++i )
                theOne = theOne.next;
                
            replacedOne = theOne;
            newOne.next = theOne.next;
            newOne.previous = theOne.previous;
            newOne.previous.next = newOne;
            newOne.next.previous = newOne;
        }
        
        return replacedOne.data;
    }
    
    public Object get(int index) throws ListLocationException
    {
        if( index < 0 || index >= size )
            throw new ListLocationException("Index " + index + " is outside acceptable parameters.");
        
        Node foundOne;
        
        if( index == size-1 )
            foundOne = last;
        else if ( index == 0 )
            foundOne = first;
        else
        {
            // find the one
            Node theOne = first.next;
            for( int i = 1; i < index; ++i )
                theOne = theOne.next;
                
            foundOne = theOne;
        }
        
        return foundOne.data;
    }
    
    public boolean contains(Object newItem) throws NullPointerException
    {
        if( newItem == null )
            throw new NullPointerException("Can't replace using a null object");
        
        boolean contains = false;
        Node thisOne = first;
        for( int i = 0; contains == false && i < size; i++ )
        {
            if( thisOne.data.equals( newItem ) )
            {
                contains = true;
                i = size;
            }
            thisOne = thisOne.next;
         }       
            
        return contains;
    }
    
    public int size()
    {
        return size;
    }
    
    public void clear()
    {
        first = null;
        last = null;
        size = 0;
    }
    
    public boolean isEmpty()
    {
        boolean isEmpty;
        if( size == 0 )
            isEmpty = true;
        else
            isEmpty = false;  
        return isEmpty;
    }   
}


