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++)
        {
            command.execute((PuzzleView)myViews.get(k), 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);
        }
    }
}
