/**
 * Lists that change and are imploemented with Cons cells.
 *
 * @author Samuel A. Rebelsky
 * @author Yvonne Palm
 * @author Jonathan "Don't I get to make suggestions, too?" Wellons
 * @author Alex "I'm Back, Be Ware" Leach
 */
public class LinkedList
  implements MutableList
{
  // +--------+---------------------------------------------------
  // | Fields |
  // +--------+

  /**
   * The front of the list.
   */
  ConsCell front;

  /**
   * The back of the list.
   */
  ConsCell back;

  /**
   * The length of the list.
   */
  int length;

  // +--------------+---------------------------------------------
  // | Constructors |
  // +--------------+

  /**
   * Create a new, empty, list.
   */
  public LinkedList()
  {
    front = null;
    back = null;
    length = 0;
  } // LinkedList()

  // +---------+--------------------------------------------------
  // | Methods |
  // +---------+

  /**
   * Determine if the list is empty
   */
  public boolean isEmpty() {
    return (null == this.front);
  } // isEmpty()

  /**
   * Update the list by adding an element to the front.
   */
  public void addToFront(Object newThingy)
  {
    this.front = new ConsCell(newThingy, this.front);
    // If the list was previously empty, ...
    if (null == this.back) {
      // The back is the same as the front.
      this.back = this.front;
    }
    ++this.length;
  } // addToFront(Object newThingy)

  /**
   * Update the list by adding an element to the end.
   */
  public void addToEnd(Object newThingy) {
    // Create a new cons cell.
    ConsCell newBack = new ConsCell(newThingy, null);
    // Special case: Empty list
    if (this.back == null) {
      this.back = newBack;
      this.front = newBack;
    }
    // Normal case 
    else {
      // Update the cdr of this.back
      this.back.myCdr = newBack;
      // Update this.back
      this.back = newBack;
    }
    ++this.length;
  } // addToEnd(Object)

  /**
   * Update the list by removing the first element.  Return
   * that element.
   */
  public Object removeFirst()
    throws Exception
  { 
    // Remember the first element.
    Object first = this.front.myCar;
    // Update the front of the list to refer to the next element.
    this.front = this.front.myCdr;
    // Do we have to do anything else if the list becomes empty?
    if (null == this.front) {
      this.back = null;
    }
    // Shrink the list
    --this.length;
    // Return the thing we previously identified as first. 
    return first;  
  } // removeFirst()

  /**
   * Remove the last element.
   */
  public Object removeLast()
    throws Exception
  {
    // Remember the last value
    Object last = this.back.myCar;
    // Special case: The list contains only one element.
    if (this.front == this.back) {
      this.front = null;
      this.back = null;
    }
    else {
      // Start at the front of the list and advance until we're
      // right before the end of the list.
      ConsCell temp = this.front;
      while (temp.myCdr != this.back) {
        temp = temp.myCdr;
      } // while
      // We can now delete the last element.
      temp.myCdr = null;
      this.back = temp;
    } // List contains more than one element
    // Shrink the list
    --this.length;
    // Return the old last value
    return last;
  } // removeLast()

  /**
   * Get the length.
   */
  public int length()
  {
    int count;
    ConsCell temp = this.front;
    while (temp != null) {
      ++count;
      temp = temp.myCdr;
    }
    return count; // STUB
  } // length()

  /**
   * Get the first element (non-destructively).
   */
  public Object getFirst()
    throws Exception
  {
    return this.front.myCar;
  } // getFirst()

  /**
   * Convert to a string for ease of printing.
   */
  public String toString()
  {
    if (null == this.front) {
      return "()";
    }
    else {
      return this.front.toString();
    }
  } // toString()

  /**
   * Make an independent copy of this list.
   */
  public MutableList copyMe() {
    return null;
  }
} // class LinkedList
