package blocks;

import framework.State;
import graph.SimpleVertex;
import java.util.Stack;

/**
 * This class represents a state of the Blocks World.
 * @author tcolburn
 */
public class BlockState extends SimpleVertex implements State {
    
    /**
     * A block state is represented by three stacks of characters, with the
     * top block on the stack top.
     * @param p the stack at place p
     * @param q the stack at place q
     * @param r  the stack at place r
     */
    public BlockState(Stack<Character> p, Stack<Character> q, Stack<Character> r) {
	this.p = p;
	this.q = q;
	this.r = r;
    }

    /**
     * A convenience constructor that allows stacks of blocks to be presented
     * as strings that are then converted to stacks of characters.
     * The first character of the string represents the top block of the stack.
     * @param p string representing place p
     * @param q string representing place q
     * @param r  string representing place r
     */
    public BlockState(String p, String q, String r) {
        this.p = stringToStack(p);
        this.q = stringToStack(q);
        this.r = stringToStack(r);
    }

    /**
     * A block state constructor that takes a state, a source place, 
     * and a target place, and creates a new state in which the block 
     * on top of the source is moved to the top of the target.
     * Precondition: the source place cannot be empty.
     * Note the use of clone() to copy the stacks.
     * @param state
     * @param source
     * @param target 
     */
    public BlockState(BlockState state, char source, char target) {
        p = (Stack<Character>)state.p.clone();
	q = (Stack<Character>)state.q.clone();
	r = (Stack<Character>)state.r.clone();
        Character block = source == 'p' ? p.pop() :
                          source == 'q' ? q.pop() : r.pop();
        Stack<Character> stack = target == 'p' ? p :
                                 target == 'q' ? q : r;
        stack.push(block);
    }

    /**
     * Indicates whether a place is empty in this state.
     * Precondition: the place must be either 'p', 'q', or 'r'.
     * @param place either 'p', 'q', or 'r'
     * @return true if the place is empty, false otherwise.
     */
    public boolean placeEmpty(char place) {

        if ( place == 'p' )
            if ( p.empty() )
                return true;
            else
                return false;
        else if ( place == 'q' )
            if ( q.empty() )
                return true;
            else
                return false;
        else  // place must be 'r'
            if ( r.empty() )
                return true;
            else
                return false;
    }

    /**
     * Getter for the stack at place p. 
     * Provided primarily for the BlockCanvas class
     * @return the stack at place p
     */
    public Stack<Character> getP() {
        return p;
    }

    /**
     * Getter for the stack at place q. 
     * Provided primarily for the BlockCanvas class
     * @return the stack at place q
     */
    public Stack<Character> getQ() {
        return q;
    }

    /**
     * Getter for the stack at place r. 
     * Provided primarily for the BlockCanvas class
     * @return the stack at place r
     */
    public Stack<Character> getR() {
        return r;
    }
    
    /**
     * Checks for equality between this block state and another object.
     * @param other the other object
     * @return true if all corresponding stacks are equal, false otherwise
     */
    public boolean equals(Object other) {
	if (this == other) return true;
	if (other == null) return false;
	if (getClass() != other.getClass()) return false;
	BlockState state = (BlockState) other;
	return (p.equals(state.p) &&
		q.equals(state.q) &&
		r.equals(state.r));
    }

    /**
     * Creates and returns a string representation of this block state.
     * @return a string representation of this block state
     */
    public String toString() {

        StringBuffer buf = new StringBuffer();

        int maxSize = Math.max(Math.max(p.size(), q.size()), r.size());

	for (int i = maxSize-1; i >= 0; i--) {
	    Character pBlock = getBlock(p,i);
	    Character qBlock = getBlock(q,i);
	    Character rBlock = getBlock(r,i);
            buf.append(" " + pBlock + " " + qBlock + " " + rBlock + " \n");
	}

        buf.append(" ----- \n p q r ");
        return buf.toString();
    }

    private static Character getBlock(Stack<Character> s, int index) {
	Character block = ' ';
	try {
	    block = ((Character)s.elementAt(index));
	}
	catch(ArrayIndexOutOfBoundsException ex) { }
	return block;
    }

    /**
     * Computes and returns a heuristic estimate of the number of moves from 
     * this state to the goal state.
     * @param goal the goal state for the problem currently being solved
     * @return the heuristic estimate of the number of moves to the goal
     */
    public int getHeuristic(State goal) {
	// You must write for A* search
        return 0;
    }

    public int hashCode() {
        // You must write for extra credit
        return 0;
    }

    /**
     * Helper method that converts a string to a stack of characters.
     * @param string a string representing a stack of blocks on a place
     * @return a stack of characters with the top character corresponding
     * to the first character in the string
     */
    private static Stack<Character> stringToStack(String string) {
        Stack<Character> stack = new Stack<Character>();
        for(int i = string.length()-1; i >= 0; i--) {
            stack.push(string.charAt(i));
        }
        return stack;
    }

    private Stack<Character> p;
    private Stack<Character> q;
    private Stack<Character> r;
}