import java.util.EmptyStackException;
import java.lang.Iterable;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class Stack<E> implements Iterable<E>
{
	// the value for default capacity
	private static final int DEFAULT_CAPACITY = 100;

	// the items stored in the stack
	private E[] items;

	// the index at the top of the stack
	private int top;

	// creates a stack with default size 100
	public Stack()
	{
		this( DEFAULT_CAPACITY );
        }

	// creates a stack with a given capacity
	public Stack( int capacity )
	{
		items = (E[]) new Object[capacity];
		top = 0;
	}

	public boolean isEmpty()
	{
		return top == 0;
	}

	public boolean isFull()
	{
		return top == items.length;
	}

	public boolean push(E value)
	{
		if (isFull())
		{
			return false;
		}
		else
		{
			items[top] = value;
			top++;
			return true;
		}
	}

	public E pop()
	{
		if (isEmpty())
		{
			throw new EmptyStackException();
		}
		else
		{
			top--;
			return items[top];
		}
	}
	
	private class StackIterator implements Iterator<E>
	{
		private int curPos;           // index/position of next item to return
		
		public StackIterator()
		{
			curPos = 0;           // first item to return on call to next()
		}

		@Override
		public boolean hasNext() 
		{	
			return curPos < top;
		}

		@Override 
		public E next() 
		{
			if (this.hasNext()) {
				E item = items[curPos];
				curPos++;
				return item;
			}
			else {
				throw new NoSuchElementException();
			}
		}

		@Override 
		public void remove() 
		{
			// Removes the *last* item given by next() based on these rules:

				// 1. can only call remove() if next() has been called

				// 2. cannot call remove() twice in a row

				// 3. the effect of remove() is to remove the last item given

			// In other words, we are *allowed* to call remove() if next() was
			// called right before; then need to wait for another call to next()
					
			// To implement these rules, add to StackIterator a boolean data member
			// that is turned ON/OFF to indicate whether it is safe to call remove()

			// Note again, that remove() removes the *last* item given by next()
			// *not* the item that next() is waiting to give
		}
		
	}

	// method of the stack -- required for Iterable
	@Override 
	public Iterator<E> iterator() 
	{
		return new StackIterator();   // return an object of our class
	}	


	// using Iterator *explicitly* with while loop
	public String toString()
	{
		String str = "";

		Iterator<E> iter = this.iterator();    // get iterator over me (the Stack)
		while ( iter.hasNext() ) {             // as long as iterator has an item to give:
			E item = iter.next();          //     get that item
			str = str + item + " ";        //     do something with the item
		}

		str = "[" + str.trim() + "]";

		return str;
	}	

	// using Iterator *implicitly* with enhanced for loop
	public String toString()
	{
		String str = "";

		for ( E item : this ) {                // for each item in me (the Stack):
			str = str + item + " ";        //     do something with the item 
		}

		str = "[" + str.trim() + "]";

		return str;
	}	
}