import java.util.ArrayList;

public class PuzzleController 
{
    private PuzzleModel myModel;
    private ArrayList   myViews;
    private ShowBoardCommand myShowCommand;
    private ViewCommand      myUndoCommand;

    /**
     * Create Controller not bound to any model --- will
     * cause null pointer exceptions if setModel(m) is not
     * called before any Controller methods are called.
     * @see #PuzzleController
     */
    
    public PuzzleController()
    {
	this(null);
    }

    /**
     * Create a controller for the designated model. Multiple
     * views can be added to a controller, but a control has
     * a single model.
     * @param model is the model for this controller
     */
    public PuzzleController(PuzzleModel model)
    {
	myModel = model;
	myViews = new ArrayList();
	myShowCommand = new ShowBoardCommand();
	myUndoCommand = new UndoCommand();
    }

    /**
     * Set the model for this controller
     * @param model is the model bound to this controller
     */
    
    public void setModel(PuzzleModel m)
    {
	myModel = m;
    }

    /**
     * Add a view to be updated by this controller when
     * the model changes.
     * @param view is the view added to this controller
     */
    
    public void addView(PuzzleView view)
    {
	myViews.add(view);
    }

    /**
     * Returns the size of the model bound to this controller
     * @return the size of the model used by this controller
     */
    public int getModelSize()
    {
	return myModel.getSize();
    }

    /**
     * Make a move (presumably results in changes to views)
     * @param move is the move made
     */
    public void makeMove(PuzzleMove move)
    {
	if (myModel.makeMove(move)){
	    updateAllViews(myUndoCommand, Boolean.TRUE);
	}
    }

    /**
     * Undo the last undoable move (just made or uncovered
     * as a result of an undo).
     */
    
    public void undoMove()
    {
	if (!myModel.undo()){
	    updateAllViews(myUndoCommand, Boolean.FALSE);	    
	}
    }

    /**
     * Update all views with the given command using
     * parameter o to command.execute(...)
     * @param command is the command executed to updated views
     * @param o will be passed to the command's execute function
     */
    private void updateAllViews(ViewCommand command, Object o)
    {
	for(int k=0; k < myViews.size(); k++){
	    PuzzleView view = (PuzzleView) myViews.get(k);
	    command.execute(view,o);
	}	
    }

    /**
     * Update view with new "board", called by model
     * @param list is the board, # elements in list is
     * getModelSize()*getModelSize().
     */
    
    public void showBoard(int[] list)
    {
	updateAllViews(myShowCommand,list);
    }


    private class ShowBoardCommand extends ViewCommand
    {
	protected void hook(PuzzleView view, Object o)
	{
	    int[] board = (int[]) o;
	    view.showGrid(board);
	}
    }

    private class UndoCommand extends ViewCommand
    {
	protected void hook(PuzzleView view, Object o)
	{
	    boolean value = ((Boolean) o).booleanValue();
	    view.setEnabledUndo(value);
	}
    }
    
}
