/* 
 * Kodkod -- Copyright (c) 2005-2011, Emina Torlak
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package kodkod.util.collections;

import java.util.EmptyStackException;
import java.util.Iterator;


/**
 * Represents a last-in-first-out (LIFO) stack of objects. 
 * The usual push and pop operations are provided, as well as a method to peek at the top item on the stack, 
 * a method to test for whether the stack is empty, and an iterator over the elements in the stack 
 * (that does not support removal).  When a stack is first created, it contains no items. 
 * 
 * @specfield size: int
 * @specfield elems: [0..size)->one T
 * @author Emina Torlak
 */
public abstract class Stack<T> implements Iterable<T> {

	/**
	 * Constructs a new stack.
	 */
	protected Stack() {}
	
	/**
	 * Returns the size of this stack.
	 * @return this.size
	 */
	public abstract int size();
	
	/**
	 * Pushes an item onto the top of this stack and returns it. 
	 * @ensures this.size' = this.size + 1 && this.elems'[0] = item &&
	 *          all i: [0..this.size) | this.elems'[i+1] = this.elems[i]
	 * @return item
	 */
	public abstract T push(T item);
	
	/**
	 * Removes the object at the top of this stack and returns that object as the value of this function.
	 * @ensures this.size' = this.size - 1 && 
	 *          all i: [1..this.size) | this.elems'[i-1] = this.elems[i]
	 * @return this.elems[0]
	 * @throws EmptyStackException  no this.elems
	 */
	public abstract T pop();

	/**
	 * Looks at the object at the top of this stack without removing it from the stack.
	 * @return this.elems[0]
	 * @throws EmptyStackException  no this.elems
	 */
	public abstract T peek();

	/**
	 * Returns the 1-based position where an object is on this stack. 
	 * If the object o occurs as an item in this stack, this method 
	 * returns the distance from the top of the stack of the occurrence 
	 * nearest the top of the stack; the topmost item on the stack is 
	 * considered to be at distance 1. The equals method is used to 
	 * compare o to the items in this stack.
	 * @return o in this.elems[int] => min(this.elems.o) + 1, -1
	 */
	public abstract int search(Object o);

	/**
	 * Returns true if the stack is empty; otherwise returns false.
	 * @return no this.elems
	 */
	public abstract boolean empty();

	/**
	 * Iterates over the items in this Stack, starting
	 * at the top of the stack and working its way down.
	 * @return iterator over the elements in this stack.
	 */
	public abstract Iterator<T> iterator();
	
	/**
	 * Returns true if both o1 and o2 are null, or 
	 * if they are non-null and 'equals' to each other.
	 * @return o1=null && o2=null || o1.equals(o2)
	 */
	static boolean equal(Object o1, Object o2) {
		return (o1==null ? o2==null : o1.equals(o2));
	}
	
	/**
	 * Returns true if o is a stack containing the same elements
	 * as this stack, in the same order.
	 * @return o in Stack  && this.elems = o.elems
	 */
	@SuppressWarnings("unchecked")
	public boolean equals(Object o) {
		if (this==o) return true;
		else if (o instanceof Stack) {
			final Stack<T> s = (Stack<T>) o;
			if (size() != s.size()) return false;
			final Iterator<T> iter0 = iterator(), iter1 = s.iterator();
			while(iter0.hasNext()) {
				if (!equal(iter0.next(), iter1.next()))
					return false;
			}
			return true;
		}
		return false;
	}
	
	/**
	 * Returns the hashcode for this stack.
	 * @return the hashcode for this stack.
	 */
	public int hashCode() {
		int code = 0;
		for(T item : this) {
			if (item!=null) code += item.hashCode(); 
		}
		return code;
	}
	
	/**
	 * Returns a string represention of this stack.
	 */
	public String toString() {
		final StringBuilder buffer = new StringBuilder("[ ");
		final Iterator<T> elems = iterator();
		if (elems.hasNext()) buffer.append(elems.next());
		while(elems.hasNext()) {
			buffer.append(", ");
			buffer.append(elems.next());
		}
		buffer.append(" ]");
		return buffer.toString();
	}
	

}