import java.util.*;

/**
 * This class can be used to "animate" the interaction between a cat
 * and mouse.  When the <tt>animate</tt> method is called, the MDP is
 * simulated with the mouse's movements being determined by the given
 * policy.  The results are printed to standard output.
 */
public class CatMouseAnimator {

    /**
     * The constructor for this class.  Takes as input an
     * <tt>mdp</tt>.  This <tt>mdp</tt> must correspond to a cat and
     * mouse world; otherwise, the constructor will almost certainly
     * crash.
     */
    public CatMouseAnimator(Mdp mdp) {
	this.mdp = mdp;
	rand = new Random();

	for (int s = 0; s < mdp.numStates; s++) {
	    CatMousePosition cm = new CatMousePosition(s);

	    if (cm.mx < minx) minx = cm.mx;
	    if (cm.cx < minx) minx = cm.cx;
	    if (cm.my < miny) miny = cm.my;
	    if (cm.cy < miny) miny = cm.cy;

	    if (cm.mx > maxx) maxx = cm.mx;
	    if (cm.cx > maxx) maxx = cm.cx;
	    if (cm.my > maxy) maxy = cm.my;
	    if (cm.cy > maxy) maxy = cm.cy;
	}	    

	cat_legal = new boolean[maxx-minx+1][maxy-miny+1];
	mouse_legal = new boolean[maxx-minx+1][maxy-miny+1];
	cheese = new boolean[maxx-minx+1][maxy-miny+1];

	for (int s = 0; s < mdp.numStates; s++) {
	    CatMousePosition cm = new CatMousePosition(s);

	    mouse_legal[cm.mx - minx][cm.my - miny] = true;
	    cat_legal[cm.cx - minx][cm.cy - miny] = true;
	    if (mdp.reward[s] > 0.)
		cheese[cm.mx - minx][cm.my - miny] = true;
	}


    }

    /**
     * This method will animate the mdp provided to the constructor
     * for <tt>numSteps</tt> steps, following policy <tt>pi</tt>.  The
     * results are printed to standard output.
     */
    public void animate(int[] pi, int numSteps) {
	int s = mdp.startState;
	print_state(s, true);
	for (int i = 0; i < numSteps; i++) {
	    double[] tp = mdp.transProb[s][pi[s]];
	    double r = rand.nextDouble();
	    int j = -1;
	    while (r >= 0.0) {
		r -= tp[++j];
	    }
	    s = mdp.nextState[s][pi[s]][j];
	    print_state(s, false);
	}
    }

    // private stuff

    private Mdp mdp;

    private Random rand;

    private int minx = Integer.MAX_VALUE;
    private int miny = Integer.MAX_VALUE;
    private int maxx = Integer.MIN_VALUE;
    private int maxy = Integer.MIN_VALUE;
    private boolean cat_legal[][];
    private boolean mouse_legal[][];
    private boolean cheese[][];

    private class CatMousePosition {
	private int mx, my, cx, cy;
	private CatMousePosition(int s) {
	    String[] coord = mdp.stateName[s].split(":");

	    mx = Integer.parseInt(coord[0]);
	    my = Integer.parseInt(coord[1]);
	    cx = Integer.parseInt(coord[2]);
	    cy = Integer.parseInt(coord[3]);
	}
    }
	    
    private void print_state(int s, boolean first) {
	CatMousePosition cm = new CatMousePosition(s);
	String indent = "    ";

	String separator = indent + "+=";
	for (int x = minx; x <= maxx; x++)
	    separator += "==";
	separator += "+";

	if (first)
	    System.out.println(separator);

	for (int y = maxy; y >= miny; y--) {
	    System.out.print(indent + "| ");
	    for (int x = minx; x <= maxx; x++) {
		System.out.print((!cat_legal[x-minx][y-miny]
				  && !mouse_legal[x-minx][y-miny])
				 ? " "
				 : (x == cm.cx && y == cm.cy
				    ? "C"
				    : (x == cm.mx && y == cm.my
				       ? "M"
				       : (cheese[x-minx][y-miny]
					  ? "z"
					  : (cat_legal[x-minx][y-miny]
					     ? "."
					     : "_")))));
		System.out.print(" ");
	    }
	    System.out.println("|");
	}

	System.out.println(separator);

    }
}

		
		
