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 PuzzleController myControl;
    private JTextField  myText;
    private ButtonPanel myButtonPanel;
    private JMenuItem   myUndo;
    
    private static final int OUR_WIDTH  = 500;
    private static final int OUR_HEIGHT = 500;
    
    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");
	JPanel panel = new JPanel(new BorderLayout());

	myControl = control;
	myControl.addView(this);

	myText = new JTextField(20);
	myButtonPanel = new ButtonPanel(myControl.getModelSize(),
					imageSource);
	
	panel.add(myButtonPanel, BorderLayout.CENTER);
	panel.add(myText,        BorderLayout.SOUTH);

	setContentPane(panel);
	setDefaultCloseOperation(EXIT_ON_CLOSE);
	makeMenu();
	constrainResize();
	
	pack();
	setSize(OUR_WIDTH, OUR_HEIGHT);
	setVisible(true);
    }

    /**
     * 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 w = getWidth();
		    int h = getHeight();
		    w = Math.max(w,h);
		    setSize(w,w);
		}
	    });
	
    }
    
    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);	
    }

    public static void main(String args[])
    {
	PuzzleController control = new PuzzleController();
	PuzzleModel model = new PuzzleModel(control,10);
	PuzzleGui pg = new PuzzleGui(control);
    }


    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();
	}
    }
}
