/**
 * This class models a stack data structure.  That is, items can only be
 * added or removed from the top of the stack.
 *
 * This stack works only with ints.  It currently has no error-checking.
 *
 * @author Zach Tomaszewski
 * @version 13 Apr 2009
 */
public class StackOfInts {

  /*
   * This class demonstrates how to use an array to store data.
   * We need to cover exception throwing before we can properly handle the
   * error-checking though.
   */

  private int[] nums;
  private int topOfStack;

  /**
   * Builds a new empty stack.
   */
  public StackOfInts() {
    nums = new int[5];
    topOfStack = -1;
  }


  /**
   * Adds the given number to the top of this stack.
   */
  public void add(int n) {
    //This first part we didn't do in lab: if the stack is full,
    // expand the array be creating a copy that's twice as large.
    if (this.topOfStack == this.nums.length - 1) {
      //create a copy twice as large
      int[] copy = new int[this.nums.length * 2];
      for (int i = 0; i <= this.topOfStack; i++) {
        copy[i] = this.nums[i];
      }
      //and replace nums with copy
      this.nums = copy;
    }

    //now it's safe to add element
    this.topOfStack++;
    this.nums[this.topOfStack] = n;
  }

  /**
   * Returns the number currently stored at the top of the stack.
   */
  public int get() {
    //XXX: With no error checking, this method will currently throw an
    //ArrayIndexOutOfBoundsException if someone tries to get()
    //on an empty stack.
    return this.nums[this.topOfStack];
  }

  /**
   * Removes and returns the top element of this stack.
   */
  public int remove() {
    //XXX: Again, without error checking, this method will throw an
    //ArrayIndexOutOfBoundsException if someone tries to remove()
    //from an empty stack.
    this.topOfStack--;
    return this.nums[this.topOfStack];
  }

  /**
   * Returns the number of elements currently stored in this stack.
   */
  public int size() {
    return topOfStack + 1;
  }


  /**
   * Tests this class by create a stack and calling its various methods.
   * Prints output to the screen with expected results in [brackets].
   */
  public static void main(String[] args) {

    System.out.println("Creating a new stack and adding (4, 6).");
    StackOfInts s = new StackOfInts();
    s.add(4);
    s.add(6);
    System.out.println("size() [2]: " + s.size());
    System.out.println("get() [6]: " + s.get());
    System.out.println("remove() [6]: " + s.remove());
    System.out.println("size() [1]: " + s.size());
    System.out.println("get() [4]: " + s.get());

    System.out.println("");
    System.out.println("Checking that adding more than default size works.");
    System.out.println("Adding 1 through 5.");
    for (int i = 1; i <= 5; i++) {
      s.add(i);
    }
    System.out.println("size() [6]: " + s.size());
    System.out.println("remove() [5]: " + s.remove());

    //The next two tests are only possible because this main method is in
    //StackOfInsts, so I have access to private variables.
    System.out.println("Array length is now [10]: " + s.nums.length);
    System.out.println("s.nums: " + java.util.Arrays.toString(s.nums));
  }

}
