import java.util.*;

/**
 * A game of solitaire Can't Stop.  Game = board + dice.
 *
 * @author Jim Glenn
 * @version 0.1 11/18/2008
 */

public class CantStopGame extends Observable
{
    /**
     * The board.
     */

    private CantStopBoard board;

    /**
     * The dice.
     */

    private FourDice dice;

    /**
     * The current state of the game.  The possible states are
     * start of turn, selecting move, acknowledging blowing it, deciding
     * whether to roll again, or finished.
     */

    private int state;
    
    /**
     * The 1-based index of the current turn.
     */

    private int turn;

    /**
     * Constants for states.
     */

    public static final int START_STATE = 0;
    public static final int MOVE_STATE = 1;
    public static final int ACK_STATE = 2;
    public static final int WON_STATE = 3;
    public static final int ROLL_STATE = 4;

    /**
     * Creates a new game.
     */

    public CantStopGame()
    {
	board = new CantStopBoard();
	dice = null;
	state = START_STATE;
	turn = 1;
    }

    /**
     * Returns a textual representation of the dice in this game.  If
     * the dice have not been rolled, this method returns the empty
     * string.
     *
     * @return a textual representation of the dice
     */

    public String getDice()
    {
	if (dice != null)
	    {
		return "" + dice.getDie(0) + dice.getDie(1) + dice.getDie(2) + dice.getDie(3);
	    }
	else
	    {
		return "";
	    }
    }

    /**
     * Rolls this game's dice.
     *
     * @throws IllegalStateException if this game is not in a state when it
     * it legal to roll the dice
     */

    public void roll()
    {
	if (state != ROLL_STATE && state != START_STATE)
	    throw new IllegalStateException("Not time to roll");

	dice = new FourDice();

	if (getLegalMoves().size() == 0)
	    state = ACK_STATE;
	else
	    state = MOVE_STATE;

	setChanged();
	notifyObservers();
    }

    /**
     * Moves this game from the blown it state to the start of the next
     * turn.
     */

    public void blowIt()
    {
	if (state != ACK_STATE)
	    throw new IllegalStateException("Haven't blown it");

	board.endTurnNoProgress();
	state = START_STATE;
	turn++;

	setChanged();
	notifyObservers();
    }

    /**
     * Ends the current turn and moves the colored markers to the
     * position of the neutral markers on the board.
     */

    public void endTurn()
    {
	if (state != ROLL_STATE)
	    throw new IllegalStateException("Haven't just moved");

	board.endTurnWithProgress();

	if (board.isGameOver())
	    {
		state = WON_STATE;
	    }
	else
	    {
		state = START_STATE;
		turn++;
	    }

	setChanged();
	notifyObservers();
    }

    /**
     * Returns a list of the legal moves from this game's current state.
     * The elements of the returned list are themselves lists that
     * contain the <CODE>Integer</CODE> labels on the columns the
     * move could use.
     *
     * @return a list of the currently legal moves
     */

    public List getLegalMoves()
    {
	List moves = new LinkedList();

	if (dice != null)
	    {
		// nothing clever here -- go over all the possible totals
		// and check which ones can be made with the dice...

		for (int tot1 = 2; tot1 <= dice.getTotal() / 2; tot1++)
		    {
			if (dice.makeTotal(tot1))
			    {
				int tot2 = dice.getTotal() - tot1;
			
				// ...and then check if we can use both...

				if (board.isLegalMove(tot1, tot2))
				    {
					List move = new LinkedList();
					move.add(new Integer(tot1));
					move.add(new Integer(tot2));
					moves.add(move);
				    }
				
				// ...or just one (yes, this should be
				// in an else, but I'd like to test
				// the students' code)
				
				if (board.isLegalMove(tot1, dice))
				    {
					List move = new LinkedList();
					move.add(new Integer(tot1));
					moves.add(move);
				    }
				
				if (tot2 != tot1 && board.isLegalMove(tot2, dice))
				    {
					List move = new LinkedList();
					move.add(new Integer(tot2));
					moves.add(move);
				    }
			    }
		    }
	    }

	return moves;
    }

    /**
     * Makes the selected move in this game.
     *
     * @param which an index in to the list returned by <CODE>getLegalMoves</CODE>
     */

    public void makeMove(int which)
    {
	List move = (List)(getLegalMoves().get(which));

	// determine whether the move uses one or two columns
	// and invoke the corresponding version of moveNeutralMarkers

	if (move.size() == 1)
	    {
		board.moveNeutralMarkers(((Integer)(move.get(0))).intValue());
	    }
	else
	    {
      		board.moveNeutralMarkers(((Integer)(move.get(0))).intValue(),
					 ((Integer)(move.get(1))).intValue());
	    }

	state = ROLL_STATE;

	setChanged();
	notifyObservers();
    }

    /**
     * Returns the current state of this game.
     *
     * @return the current state
     */

    public int getState()
    {
	return state;
    }

    /**
     * Returns the current board in this game.
     *
     * @return the board
     */


    public CantStopBoard getBoard()
    {
	return board;
    }

    /**
     * Returns the 1-based index of the current turn.
     *
     * @return the current turn
     */

    public int countTurns()
    {
	return turn;
    }
}


