/*
 * GenericStackTest.java
 *
 * Created on June 7, 2004, 2:04 PM
 */

import java.util.Iterator;

/**
 *
 * @author  cpresser
 */

//Java of course already has a Stack class, however it makes for an
//easy to build/easy to understand example of building a Generic collection
//...hopefully
public class GenericStackTest<T> implements Iterable<T> {
    protected int MIN_SIZE = 4; //I'll declare a constant for this, because
                                 // some of my students might be watching.
    
    //place to store the data    
    private T[] data;
    private int top;
    
    /** Creates a new instance of GenericStackTest */
    public GenericStackTest() {
        /*
         Since you can't create a new T[] array, we'll just cast an Object[]
        Incidentally, this generates a compiler warning, but is how Java's
        own implementation of ArrayList does it. :( groan...
        The warning indicates that the code is not type safe (i.e. it
        could cause run time type failures). 
        So it is up to us to write code that ensures only the correct type 
        items are put in the array. Therefore the only way to add items is
        to use the method push. The data array is private so it cannot be
        modified by subclasses.
         */
        data = (T[])new Object[MIN_SIZE];
        
        //this is illegal
        // data = new T[MIN_SIZE];
        
        top = -1;
    }
    
    //If you have to ask about these methods, perhaps you should be doing 
    //a different tutorial.
    public T pop(){
        if(isEmpty()){
            //we REALLY should throw an exception here, but we won't add that 
            //detail
            return null; 
        }
        T result = data[top];
        data[top] = null;
        top--;
        return result;  
    }
    
    public void push(T item){
        if(top == data.length - 1){
            //recreate the array (double the length)
            T[] newData = (T[])new Object[data.length * 2];
            for(int i = 0; i < data.length; i++){
                newData[i] = data[i];
            }
            data = newData;
        }
        top++;
        data[top] = item;
    }
    
    public boolean isEmpty(){
        return top < 0;
    }
    
    //print out a representation of the stack
    public void dump(){
        for(int i = top; i >= 0; i--){
            System.out.println(i + ": " + data[i]);          
        }
        /*
         * This is a place where the new loop would not be appropriate:
         *     e.g.  for(T item: data) {...}
         * - The new for loop goes from 0..length-1, this example goes
         *   backwards.
         * - The new for loop would include all items in data not just
         *   the "valid" ones.
         * - We would need to keep track of an index i in order to print the 
         *   line number anyway.
         *
         * We could, however, make in iterator and make this class iterable
         * to use the new loop. See example and iterator class below.
        */
    }
    
    //get the capacity of the stack
    public int capacity(){
        return data.length;
    }
    
    //just to be thorough
    public int size(){
        return top + 1;
    }
    
    //allows this class to be used in a foreach styl loop
    public Iterator<T> iterator(){
        //create an iterator
        return new GenericStackTestIterator();
    }

    //an iterator for this class
    private class GenericStackTestIterator implements Iterator<T>{
        int index;
        GenericStackTestIterator(){
            index = top;
        }
        
        public boolean hasNext(){
            return index >= 0;
        }
        
        public T next(){
            if(index < 0){
                throw new java.util.NoSuchElementException();
            }
            T item = data[index];
            index--;
            return item;
        }
        
        public void remove(){
            throw new UnsupportedOperationException(
                    "Remove elements from a stack using pop()");
        }
    }
        
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        //create a new one that holds Strings
        GenericStackTest<String> stack = new GenericStackTest<String>();
        
        System.out.println("Capacity: " + stack.capacity());
        stack.push("Item 1");
        stack.push("Item 2");
        stack.push("Item 3");
        stack.push("Item 4");
        stack.push("Item 5");
        System.out.println("Capacity: " + stack.capacity());
        String item = stack.pop();
        System.out.println("Pop: " + item);
        System.out.println("Pop: " + stack.pop());
        stack.dump();
        System.out.println("-----------------");
        //curious that this won't compile
       
        for(String s: stack){
            System.out.println(s);
        }               
    }
}