import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.event.*;
import javax.swing.*;


/**
 * This is a view for a sliding n-puzzle with one image serving as the soruce
 * for many different pieces each of which can slide. Clients can also specify a
 * null image which results in simple, numbered and colored squares being used
 * for each piece of the n-puzzle.
 * <P>
 * This is the view, it talks to a controller which mediates between a view (or
 * several) and the view(s) model.
 * 
 * @author Owen Astrachan
 */
public class PuzzleGui extends JFrame implements PuzzleView
{
    private static final int OUR_WIDTH = 500;
    private static final int OUR_HEIGHT = 500;

    private PuzzleController myControl;
    private ButtonPanel myButtonPanel;
    private JTextField myText;
    private JMenuItem myUndo;


    public PuzzleGui (PuzzleController control)
    {
        this(control, null);
    }

    /**
     * Create a gui with a model and an image (image optional)
     * 
     * @param imageSource is null or the url/file for an image
     * @param model is the model for this view
     */
    public PuzzleGui (PuzzleController control, String imageSource)
    {
        setTitle("OOGA Puzzle");
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        myControl = control;
        myControl.addView(this);

        myText = new JTextField(20);
        myButtonPanel = new ButtonPanel(myControl.getModelSize(), imageSource);

        JPanel panel = new JPanel(new BorderLayout());
        panel.add(myButtonPanel, BorderLayout.CENTER);
        panel.add(myText, BorderLayout.SOUTH);
        setContentPane(panel);
        makeMenu();
        constrainResize();

        pack();
        setSize(OUR_WIDTH, OUR_HEIGHT);
        show();
    }


    /**
     * Required by interface, displayes text message.
     * 
     * @param text is to be displayed by view
     */
    public void showText (String text)
    {
        myText.setText(text);
    }

    /**
     * Required by interface, enables/disables undo invoker
     * 
     * @param value sets the status of the undo (button, menu-item, etc.)
     */
    public void setEnabledUndo (boolean value)
    {
        myUndo.setEnabled(value);
    }

    /**
     * Required by interface, show the grid that's the model
     * 
     * @param list represents the model
     */
    public void showGrid (int[] list)
    {
        myButtonPanel.setGrid(list);
    }

    /**
     * Make resizing result in a square view, so "snap" to squareness before
     * resizing takes effect.
     */
    private void constrainResize ()
    {
        addComponentListener(new ComponentAdapter()
            {
                public void componentResized (ComponentEvent e)
                {
                    int size = Math.max(getWidth(), getHeight());
                    setSize(size, size);
                }
            });
    }

    private void makeMenu ()
    {
        JMenu menu = new JMenu("Puzzle");
        myUndo = new JMenuItem(new AbstractAction("Undo")
            {
                public void actionPerformed (ActionEvent e)
                {
                    myControl.undoMove();
                }
            });
        setEnabledUndo(false);
        menu.add(myUndo);

        menu.add(new AbstractAction("Quit")
            {
                public void actionPerformed (ActionEvent e)
                {
                    System.exit(0);
                }
            });
        JMenuBar menubar = new JMenuBar();
        menubar.add(menu);
        setJMenuBar(menubar);
    }




    class ButtonPanel extends JPanel
    {
        private JButton myButtons[];

        /**
         * Create a button panel consisting of nButtons per side of a square
         * (total # buttons is nButtons*nButtons) using the designated
         * imageSource (if not null).
         * 
         * @param nButtons is the number of buttons PER SIDE
         * @param imageSource is the source of the image for this panel
         */
        ButtonPanel (int nButtons, String imageSource)
        {
            // construct superclass and make button array
            super(new GridLayout(nButtons, nButtons));
            myButtons = new JButton[nButtons * nButtons];

            // load the image if one is specified
            Image image = null;
            if (imageSource != null)
            {
                image = ImageFactory.getImage(this, imageSource);
            }

            // make listeners for buttons in panel
            // first one to show text in this GUI
            ActionListener textDisplayer = new ActionListener()
                {
                    public void actionPerformed (ActionEvent e)
                    {
                        showText(e.getActionCommand());
                    }
                };

            // make listener to do the move chosen by user
            ActionListener moveMaker = new ActionListener()
                {
                    public void actionPerformed (ActionEvent e)
                    {
                        int val = Integer.parseInt(e.getActionCommand());
                        myControl.makeMove(new PuzzleMove(val));
                    }
                };

            makeButtons(image, textDisplayer, moveMaker);
        }

        /**
         * Makes the buttons for this GUI/view. The number of buttons is
         * determined my the size of the array myButtons. This helper function
         * takes some of the busy work out of the ButtonPanel constructor.
         */
        private void makeButtons (Image image,
                                  ActionListener textDisplayer,
                                  ActionListener moveMaker)
        {
            for (int k = 0; k < myButtons.length; k++)
            {
                String label = "" + k;
                String iLabel = label;
                if (k == myButtons.length - 1)
                {
                    iLabel = PuzzleConsts.BLANK;
                }
                Icon icon;
                if (image == null)
                {
                    icon = new PlainPuzzleIcon(iLabel, myControl.getModelSize());
                }
                else
                {
                    icon = new ImagePuzzleIcon(image, iLabel,
                                               myControl.getModelSize());
                }
                // create button with icon
                myButtons[k] = new JButton(icon);
                myButtons[k].setActionCommand(label);
                myButtons[k].addActionListener(textDisplayer);
                myButtons[k].addActionListener(moveMaker);

                add(myButtons[k]);
            }
        }

        /**
         * Set all the buttons by re-displaying them all in the right order.
         * First we remove all the components in this panel (that's the
         * buttons). Then we add the buttons to this panel in the right order
         * based on what the ordering of the buttons in the model is. Finally,
         * we revalidate so the buttons are shown (GUI will redraw as a result
         * of the revalidate).
         */
        public void setGrid (int list[])
        {
            removeAll();
            for (int k = 0; k < list.length; k++ )
            {
                add(myButtons[list[k]]);
            }
            revalidate();
        }
    }
}
