/**
 * An implementation of general lists using arrays.
 *
 * @author Samuel A. Rebelsky
 * @author CSC153 2004S
 * @version 1.0 of April 2004
 */
public class ArrayBasedGeneralList
  implements GeneralList
{
  // +--------------+------------------------------------------------------
  // | Design Notes |
  // +--------------+

/*

o The elements of the list are stored in an array from positions
  0 to length-1.

o Positions in the list are simply numbers (indices).

o We keep track of positions by having a field that refers to the
  associated list.
   
 */

  // +--------+------------------------------------------------------------
  // | Fields |
  // +--------+

  /**
   * The array that stores the contents of the list.
   */
  Object[] contents;

  /**
   * The length of the list (not the same as the length of the
   * array).
   */
  int length;


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

  /**
   * Create a new empty list.
   */
  public  ArrayBasedGeneralList() {
    this.contents = new Object[100];
    this.length = 0;
  } // ArrayBasedGeneralList()

  // +-----------+---------------------------------------------------------
  // | Observers |
  // +-----------+

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

  /**
   * Determine the length of the list.
   */
  public int length() {
    return this.length;
  } // length()

  /**
   * Get the object at a particular position in the list.
   *
   * Pre:
   *   (1) The position is valid. [Unchecked]
   *   (2) The position is associated with this list. [Unchecked]
   */
  public Object get(Position pos) {
    // Extract the index from the position.
    int ind = ((ArrayPosition) pos).index;
    // Return whatever is at that index.
    return this.contents[ind];
  } // get(Position)

  /**
   * Get the first position in the list.
   *
   * Pre:
   *   (1) The list is nonempty.
   */
  public Position first() {
    return new ArrayPosition(this, 0);
  } // first()

  /**
   * Get the last position in the list.
   *
   * Pre:
   *   (1) The list is nonempty.
   */
  public Position last() {
    return new ArrayPosition(this, this.length-1);
  } // last()

  /**
   * Determine if a position is the first position in this list.
   *
   * Pre:
   *   (1) The position is valid.
   *   (2) The position is associated with this list.
   */
  public boolean isFirst(Position pos) {
    return ((ArrayPosition) pos).index == 0;
  } // isFirst(Position)

  /**
   * Determine if a position is the last position in this list.
   *
   * Pre:
   *   (1) pos is valid.
   *   (2) pos is associated with this list.
   */
  public boolean isLast(Position pos) {
    return ((ArrayPosition) pos).index == length-1;
  } // isLast(Position)

  /**
   * Get the next position in this list.
   *
   * Pre:
   *   (1) pos is valid.
   *   (2) pos is associated with this list.
   *   (3) pos is not the last position in this list.
   */
  public Position next(Position pos) {
    return new ArrayPosition(this, ((ArrayPosition) pos).index + 1);
  } // next(Position)

  /**
   * Get the previous position in this list.
   *
   * Pre:
   *   (1) pos is valid.
   *   (2) pos is associated with this list.
   *   (3) pos is not the first position in this list.
   */
  public Position prev(Position pos) {
    return new ArrayPosition(this, ((ArrayPosition) pos).index - 1);
  } // prev(Position)

  /**
   * Determine if a position is a associated with this list.
   */
  public boolean belongs(Position pos) {
    return ( (pos instanceof ArrayPosition) 
             && (((ArrayPosition) pos).base == this) );
  } // belongs(Position)


  // +----------+----------------------------------------------------------
  // | Mutators |
  // +----------+

  /**
   * Add an element to the list.
   *
   * Post: 
   *   (1) The length of the list has increased by 1.
   *   (2) The list now contains another instance of newelt.
   *   (3) All positions previously associated with this list
   *       are now invalid.
   */
  public void add(Object newelt) {
    // Cross our fingers and assume the array is big enough
    if (this.length == this.contents.length)
      throw new Error("Aaagh.  I ran out of memory because I chose a really stupid implementation of lists.");
    // Stick it on the end of the part of the array that contains the list
    // Increase the length
    this.contents[this.length++] = newelt;
  } // add(Object)

  /**
   * Remove all elements from the list.
   *
   * Post: 
   *   (1) The list contains no elements.
   *   (2) All positions previously associated with this list 
   *       are now invalid.
   */
  public void clear() {
    // STUB
  } // clear()

  /**
   * Remove an element at a specified position from the list.
   *
   * Pre:
   *   (1) The position is valid.
   *   (2) The position is associated with this list.
   *   (3) get(pos) returns obj.
   * Post:
   *   (1) The length of the list has decreased by 1.
   *   (2) The list contains one fewer instance of obj.
   *   (3) All positions previously associated with this list 
   *       are now invalid.
   *   (4) The value returned is obj.
   */
  public Object remove(Position pos) {
    // Strategy one: Make a whole new array: O(n)
    // Strategy two: Have a hole in the array: Not allowed
    // Strategy three: Shift everything left/up: O(n)
    // Strategy four: Shove the last element in the position you're removing
    int ind = ((ArrayPosition) pos).index;
    Object removed = this.contents[ind];
/*
    --this.length;
    for (int i = ind; i < this.length; ++i)
      this.contents[i] = this.contents[i+1];
    this.contents[this.length] = null;
 */
    --this.length;
    this.contents[ind] = this.contents[this.length];
    this.contents[this.length] = null;
    return removed;
  } // remove(Position)

  /**
   * Replace the element at a specified position in the list.
   *
   * Pre:
   *   (1) The position is valid.
   *   (2) The position is associated with this list.
   *   (3) get(pos) returns oldobj.
   * Post:
   *   (1) get(pos) now returns newobj.
   *   (2) The value returned is oldobj.
   */
  public Object replace(Position pos, Object newobj) {
    // STUB
    return null;
  } // replace(Position, Object)

} // class ArrayBasedGeneralList

/**
 * A position in an array.
 */
class ArrayPosition
  implements Position
{
  // +--------+------------------------------------------------------------
  // | Fields |
  // +--------+

  /**
   * The list that contains this position.
   */
  ArrayBasedGeneralList base;

  /**
   * The index of the element in the array.
   */
  int index;

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

  /**
   * Create a new position in base using index index.
   */
  public ArrayPosition(ArrayBasedGeneralList base, int index)
  {
    this.base = base;
    this.index = index;
  } // ArrayPosition(ArrayBasedGenneralList, int)

} // ArrayPosition

