/*
 *
 * Copyright (c) 2007
 * Adrian Michel
 * http://www.tradery.com
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  Adrian Michel makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
*/


package  com.tradery.collections;

import java.util.Enumeration;

/**
 * A doubly linked list of objects
 * 
 * Note: this is not used anywhere and it has not been tested.
 * It looks like there are flaws in the copy constructor and append/prepend lists
 */
public class List implements Cloneable
{
  /**
   * The first element in the list
   */
  private Node first = null;
  /**
   * The last element in th elist
   */
  private Node last = null;
  
  /**
   * A node in the list.
   * 
   * Has links to the previous and next node in the list, and holds a reference
   * to the payload object
   */
  private class Node
  {
    /**
     * Link to the previous node
     */
    private Node prev = null;
    /**
     * Link to the next node
     */
    private Node next = null;
    /**
     * The payload object
     */
    private Object obj;
    
    /**
     * Constructor taking a payload object
     * 
     * @param _obj
     */
    Node( Object _obj ) { obj = _obj; }
    /**
     * Creates a new node with a reference the the payload object of anothre node.
     * 
     * @param node
     */
    Node( Node node ) { obj = node.getObject(); }
    
    /**
     * set/get methods for pref/next references
     * 
     * @param node
     */
    void setNext( Node node ) { next = node; }
    void setPrev( Node node ) { prev = node; }
    
    Node getNext() { return next; }
    Node getPrev() { return prev; }
    

    /**
     * Indicate if the node has a next/prev node
     * 
     * @return true if it has, false if it does not.
     */
    boolean hasNext() { return next != null; }
    boolean hasPrev() { return prev != null; }
    
    /**
     * Returns the payload object
     * 
     * @return The payload object
     */
    Object getObject() { return obj; }
    
    public Object clone()
    {
      return null;
    }
  }
  
  /**
   * Implements an Enumerator object that allows a forward traversal of the tree
   */
  private class ForwardEnumerator implements Enumeration
  {
    private Node crtNode = null;
    
    public ForwardEnumerator( List list ) { crtNode = list.getFirst(); }
    
    public boolean hasMoreElements() { return crtNode != null; }
    public Object nextElement()
    {
      if( crtNode != null )
        return ( crtNode = crtNode.getNext() ).getObject();
      else
        return null;
    }           
  }
  
  /**
   * Appends a new node to the list
   * 
   * @return 
   */
  private class ReverseEnumerator implements Enumeration
  {
    private Node crtNode = null;
    
    public ReverseEnumerator( List list ) { crtNode = list.getLast(); }
    
    public boolean hasMoreElements() { return crtNode != null; }
    public Object nextElement()
    {
      if( crtNode != null )
        return ( crtNode = crtNode.getPrev() ).getObject();
      else
        return null;
    }           
  }
  
  void append( Object obj ) 
  {
    Node node = new Node( obj );
    Node oldLast = last;
    
    if( last != null )
    {
      last = node;
      oldLast.setNext( last );
    }
    last.setPrev( oldLast );
  }
  
  /**
   * Prepends a new node to the list
   * 
   * @param obj    The new node
   */
  void prepend( Object obj )
  {
    Node node = new Node( obj );
    Node oldFirst = first;
    
    if( first != null )
    {
      first = node;
      oldFirst.setPrev( first );
    }
    first.setNext( oldFirst );
  }
  
  /**
   * Gets the node at the beginning of the list
   * 
   * @return the node at the beginning of the list
   */
  Node getFirst() { return first; }
  /**
   * Gets the node at the end of the list
   * 
   * @return The node at the and of the list
   */
  Node getLast() { return last; }
  
  /**
   * Returns a forward enumerator initialized to the beginning of the list
   * 
   * @return The enumerator object
   */
  Enumeration getForwardEnumeration() { return new ForwardEnumerator( this ); }
  /**
   * Returns a reverse enumerator initialized to the end of the list
   * 
   * @return The reverse enumerator
   */
  Enumeration getReverseEnumeration() { return new ReverseEnumerator( this ); }
  
  /**
   * Appends a list to the current list. The old list disappears as a standalone list
   * 
   * @param list   The new list
   * @return The combined list
   */
  List append( List list )
  {
    append( list.getFirst() );
    last = list.getLast();
    list.empty();
    return this;
  }
  
  /**
   * Prepends a list to the current list. The old list disappears as a standalone list
   * 
   * @param list   The list to be prepended
   * @return The combined list
   */
  List prepend( List list )
  {
    prepend( list.getLast() );
    first = list.getFirst();
    list.empty();
    return this;
  }
  
  /**
   * Prepends a copy of the list to the current list (preserves the list).
   * 
   * @todo make sure it makes sense
   * @param list   The list to be prepended
   * @return The combined list
   */
  List prependAndPreserve( List list )
  {
    prepend( new List( list ) );
    return this;
  }
  
  List appendAndPreserve( List list )
  {
    append( new List( list ) );
    return this;
  }
  
  public void empty() { first = last = null; }
  
  /**
   * Copy constructor. Creates a new list based on an existing list.
   * 
   * Note: this may not work as expected - to check.
   * 
   * @param list
   */
  List( List list )
  {
    for( Enumeration e = list.getForwardEnumeration(); e.hasMoreElements(); )
    {
      append( (Node)e.nextElement() );
    }
  }
  /**
   * Default constructor. Creates an empty list
   */
  List(){}
}