/*
 * DoublyLinkedList.java
 *
 * Created on December 11, 2000, 2:24 PM
 */

package com.limegroup.gnutella.util;

import com.sun.java.util.collections.*;
import com.limegroup.gnutella.Assert;

/**
* A classic doubly-linked list.  Unlike the LinkedList class in the JDK, this
* provides way a way to refer to elements of the list (each of type ListElement)
* directly, avoiding linear-time searches when you wish to remove an element.
* This currently only has a minimal set of operations.<p>
*
* <b>This class is not thread-safe.</b> All the access to the list should be
* synchronized externally if required.
*
* @author Anurag Singla initial revision
* @author Christopher Rohrs bug fix, specification cleanup, and unit tests
*/
public class DoublyLinkedList
{    
    /*
     * This linked list can be visualized as
     * null<--start<-->e1<-->e2<-->...<-->en<-->last-->null,
     * where e1, e2,...en are the stored elements in the list 
     */ 

    /**
     * points to the first element in the list (thru its next element) 
     * INVARIANT: prev, & value fields are always null for this
     */
    private ListElement start;

    /**
     * points to the last element in the list (thru its prev element)
     * INVARIANT: next, & value fields are always null for this
     */
    private ListElement last;
    
    /** Creates new empty DoublyLinkedList */
    public DoublyLinkedList()
    {
        //allocate space for both start & last pointers
        //The prev & next fields will be pointing to null at this point
        //in both the references
        start = new ListElement(null);
        last = new ListElement(null);
    
        //since no elements right now, make start & last point to each other
        start.next = last;
        last.prev = start;
    }



    /**
     * Inserts an object at the end of the list, returning its 
     * corresponding element.
     * @param value the value of the new element.
     * @return the element holding value.
     */
    public ListElement addLast(Object value)
    {
        ListElement element=new ListElement(value);

        //else insert at the end
        element.prev = last.prev;
        element.next = last;
        element.prev.next = element;
        last.prev = element;

        return element;
    }

    /**
     * Removes and returns the first element from the list
     * @return The element removed, or null if none present
     */
    public ListElement removeFirst()
    {
        //if no element in the list, return null
        if(start.next == last)
            return null;

        //else store the element to be removed/returned
        ListElement removed = start.next;
    
        //adjust the pointers
        start.next = start.next.next;
        start.next.prev = start;
    
        //return the removed element
        return removed;
    }

    /**
     * Removes the specified element from the list
     * @param element The element to be removed.  This must be an element
     *  of this.
     */
    public void remove(ListElement element)
    {
        //if null element or invalid state, return false
        //No element in the list is gonna have any of the pointers null
        if(element == null || element.prev == null || element.next == null)
            return;
    
        //also start and last cant be removed
        if(element == start || element == last)
            return;
    
        //adjust the pointers to remove the element from the list
        element.prev.next = element.next;
        element.next.prev = element.prev;
    }

    /**
     * Removes all entries from this list
     */
    public void clear() 
    {
        //since no elements, make start & last point to each other
        start.next = last;
        last.prev = start;
    }


    /* 
     * Returns an iterator that yields the ListElement's in this, 
     * each once, in order, from head to tail.  Call getValue() on
     * each element to get the values in this.
     *     @requires this not modified while iterator in use.
     */
    public Iterator iterator() {
        return new DoublyLinkedListIterator();
    }

    /**
     * Returns true if this contains the given ListElement.
     */
    public boolean contains(ListElement e) {
        for (Iterator iter=iterator(); iter.hasNext(); ) {
            ListElement e2=(ListElement)iter.next();
            if (e.equals(e2))
                return true;
        }
        return false;
    }
    
    private class DoublyLinkedListIterator extends UnmodifiableIterator {
        /** The next element to yield, or last if done. */
        private ListElement next=start.next;

        public boolean hasNext() {
            return next!=last;
        }

        public Object next() {
            if (! hasNext())
                throw new NoSuchElementException();
            ListElement ret=next;
            next=next.next;
            return ret;
        }
    }

    /**
     * An element of the linked list.  Immutable.
     */
    public static class ListElement
    {
        /**
         * The key/object it stores
         */
        Object key;
    
        /**
         * Refernce to the previous element in the list
         */
        ListElement prev;
    
        /**
         * Refernce to the next element in the list
         */
        ListElement next;
    
        /**
         * creates a new instance, with the specified key
         * @param key The key/value to be stored in this list element
         */
        ListElement(Object key)
        {
            //store the object
            this.key = key;
            //make both the forward & backward pointers null
            prev = null;
            next = null;
        }
    
        /**
         * returns the key stored in this element
         * @return the key stored in this element
         */
        public Object getKey()
        {
            return key;
        }

    }//end of class ListElement
}

