import Node;
import CursoredList;
import Comparator;
import IncomparableException;

/**
 * An implementation of cursored lists using linked lists, with nodes
 * that connect to each other.  Written as part of the solution to
 * HW4 of CSC152 99F and also for exam 3 of the same course.
 *
 * Specific documentation for each method can be found in the CursoredList
 * interface.
 *
 * @author Samuel A. Rebelsky
 * @version 1.0 of November 1999
 */
public class CursoredLinkedList
  implements CursoredList
{

  // +--------+--------------------------------------------------
  // | Fields |
  // +--------+
  
  /**
   * The node that contains the first element of the list.  This is set to
   * null (or, more precisely, Node.nil()) when the list is empty.
   */
  protected Node front;
  
  /**
   * The node that contains the current element of the list.  When this
   * is null, the position is unknown.
   */
  protected Node cursor;
  
  // +--------------+--------------------------------------------
  // | Constructors |
  // +--------------+
  
  /**
   * Create a new empty list.
   */
  public CursoredLinkedList() {
    front = Node.nil();
    cursor = null;
  } // CursoredLinkedList()
  
  // +---------+-------------------------------------------------
  // | Methods |
  // +---------+
  
  /**
   * Add an element to the end of the list.
   */
  public void addToEnd(Object element) {
    // Special case: The list is empty.
    if (Node.isEmpty(front)) {
      // Create a new node for the front of the list.
      front = new Node(element, Node.nil());
    }
    // Normal case: The list is nonempty
    else {
      // Find the last element in the list.
      Node finder = front;
      while (!(Node.isEmpty(finder.getNext()))) {
        finder = finder.getNext();
      } // while
      // Add after that
      finder.setNext(new Node(element, Node.nil()));
    } // The list is nonempty
  } // addToEnd(Object)

  /**
   * Add an element to the front of the list.
   */
  public void addToFront(Object element) {
    // Special case: The list is empty.
    if (Node.isEmpty(front)) {
      front = new Node(element, Node.nil());
    }
    // Normal case: The list is nonempty.
    else {
      // (1) Create a new node for the front of the list.
      // (2) Set its next element to the old front.
      // (3) Update front to refer to the new node.
      front = new Node(element, front);
    }
  } // addToFront(Object)

  /**
   * Add an element after the current element.
   */
  public void addAfterCursor(Object element) {
    // Don't have to worry about the special case in which the
    // list is empty, since the precondition says "the position
    // of the cursor is known", and the cursor has no known
    // position in the empty list.

    // (1) Create a new node whose next node is cursor's next element.
    // (2) Make that node the cursor's new next element.
    cursor.setNext(new Node(element, cursor.getNext()));
  } // addAfterCursor(Object)
 
  /**
   * Delete the current element.
   */
  public void delete() {
    // Special case: The cursor is at the front of the list.
    if (cursor == front) {
      front = front.getNext();
      // The position of the cursor is unspecified.
      cursor = null;
    } // cursor at front of the list
    // General case: The cursor is not at the front of the list.
    else {
      // Find the node that precedes the cursor.
      // Note that the loop must terminate if the cursor is in the list, 
      // and the cursor being in the list is a precondition.
      Node finder = front;
      while (finder.getNext() != cursor) {
        finder = finder.getNext();
      }
      // Skip over the cursor'd element.
      finder.setNext(cursor.getNext());
      // Let Java clean up the deleted node.
      cursor = null;
    } // general case
  } // delete()

  /**
   * Move the cursor to the next element in the list equal
   * to the specified element.
   */
  public void find(Object findMe)
    throws Exception
  {
    // If we need the NEXT equal element, we need to start after
    // the current element.
    cursor = cursor.getNext();
    // Keep going until we run off the list or find a match
    while ( (cursor != null) && (!findMe.equals(cursor.getContents())) ) {
      cursor = cursor.getNext();
    }
    // Have we run off the end of the list?
    if (cursor == null) {
      throw new Exception("Cannot find another copy of '" + findMe + "'");
    }
  } // find(Object)

  /**
   * Move the cursor to the next element in the list equal
   * to the specified element, using compare for comparisons.
   */
  public void find(Object findMe, Comparator compare)
    throws Exception
  {
    // Keep going until we run off the list or find a match.
    // This doesn't match the code above because the equals
    // method can throw an exception.
    boolean done = false;
    while (!done) {
      // If we need the NEXT equal element, we need to start after
      // the current element.
      cursor = cursor.getNext();
      try {
        done = ( (cursor == null)
                 || (compare.equals(findMe, cursor.getContents())) );
      }
      catch (IncomparableException e) {
        // Do nothing.  If the two things weren't comparable, so
        // they can't be equal.  If we've already run off the end
        // of the list, cursor will be null, so we don't call the
        // exceptional code.
      }
    } // while
        
    // Have we run off the end of the list?
    if (cursor == null) {
      throw new Exception("Cannot find another copy of '" + findMe + "'");
    }
  } // find(Object, Comparator)

  /**
   * Move the cursor to the front of the list.
   */
  public void front() {
    cursor = front;
  } // front()

  /** 
   * Get the current element of the list.
   */
  public Object getCurrent() {
    return cursor.getContents();
  } // getCurrent()

  /**
   * Advance the cursor to the next elmeent.
   */
  public void advance()
    throws Exception
  {
    cursor = cursor.getNext();
    if (cursor == null) {
      throw new Exception("Ran off of the list");
    }
  } // advance()

} // class CursoredLinkedList

