package gizmoball.ui;

import gizmoball.ui.r2.R2GameBoardComponent;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.border.LineBorder;

/**
 * A frame containing the GUI for Gizmoball.  The GUI is
 * divided into four areas: the options area, the palette
 * area, the game area, and the info area.<p>
 * 
 * In playing mode, only the options area and the game
 * area are visible.  In editing mode, all the areas are
 * visible.<p>
 * 
 * The options area contains the menus of buttons that 
 * perform universal functions (load, save, mode-toggle, 
 * exit) and mode-specific functions (playing: play/pause; 
 * editing: snap to grid, display connections out/in, undo, 
 * reset).<p>
 * 
 * The game area holds the gameboard component and a status
 * bar.  Errors and other messages to the user are reported
 * via the status bar.<p>
 * 
 * The palette area and the info area are only displayed in
 * editing mode.  The palette depicts the set of placeable
 * gizmos; through these, the user is able to add gizmos to
 * the board.  The info area displays detailed information
 * about the properties of an item in focus.  Part of this
 * information is a property table through which one can
 * view or set the values of any of the properties of a 
 * gizmo.<p>
 * 
 * 
 * @see AbstractGameBoardComponent
 * @author Albert Leung
 * @version $Id: GizmoballFrame.java,v 1.17 2004/04/27 19:10:10 aleung Exp $
 * 
 */
public class GizmoballFrame extends JFrame {

	private JPanel pnOptsMenu, pnUnivOpts, pnEditOpts, pnGPalette, pnGameCore, pnProperty;
	private JPanel pnGameOpts;
	private JButton btLoad, btSave, btMode, btQuit, btUndo, btPause;
	private JCheckBox ckSnapToGrid, ckDispIn, ckDispOut;
	private JLabel lbDisplay, lbGPalette, lbStatus;
	private JFileChooser chooser;

	private AbstractGameBoardComponent gameboardComponent;

	private Timer timer;

	public GizmoballFrame() {
		super("6.170 Gizmoball");
		
		//look and feel
		try {
			String lookAndFeelClass =
				UIManager.getSystemLookAndFeelClassName();
				UIManager.setLookAndFeel(lookAndFeelClass);
		} catch (Exception e) {
			// if an exception is thrown,
			// then the default (metallic) look and feel will be used
			// it is pretty unlikely that this will happen
			e.printStackTrace();
		}
		
		this.setBounds(200, 200, 500, 500);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		Container mainpane = this.getContentPane();
		mainpane.setLayout(new BorderLayout());
		
		pnOptsMenu = new JPanel(new BorderLayout());
		pnUnivOpts = new JPanel(new FlowLayout());
		pnEditOpts = new JPanel(new FlowLayout());
		pnGameOpts = new JPanel(new FlowLayout());
		pnGPalette = new JPanel();
		pnGameCore = new JPanel(new BorderLayout());
		pnProperty = new JPanel();
		
		btLoad = new JButton("Load...");
		btSave = new JButton("Save...");
		btMode = new JButton("Play");
		btQuit = new JButton("Quit");
		btUndo = new JButton("Undo");
		btPause = new JButton("Start");
		btLoad.addActionListener(new LoadListener());
		btSave.addActionListener(new SaveListener());
		btMode.addActionListener(new ModeListener());
		btQuit.addActionListener(new QuitListener());
		btUndo.addActionListener(new UndoListener());
		btPause.addActionListener(new PauseListener());
		
		ckSnapToGrid = new JCheckBox("Snap to grid", true);
		ckDispIn = new JCheckBox("in", false);
		ckDispOut = new JCheckBox("out", true);
		ckSnapToGrid.addActionListener(new SnapListener());
		ckDispIn.addActionListener(new CkInListener());
		ckDispOut.addActionListener(new CkOutListener());
		
		lbDisplay = new JLabel("Display connections: ");
		lbGPalette = new JLabel("Gizmos");
		lbStatus = new JLabel(" Under Construction");
		lbStatus.setBorder(new LineBorder(Color.GRAY));
		
		// TODO : correctly set up the properties table
		JLabel properties = new JLabel("Properties go here");
		pnProperty.add(properties);
		
		//fill in menu panels
		pnUnivOpts.add(btLoad);
		pnUnivOpts.add(btSave);
		pnUnivOpts.add(btMode);
		pnUnivOpts.add(btQuit);
		
		pnEditOpts.add(ckSnapToGrid);
		pnEditOpts.add(new JSeparator(SwingConstants.VERTICAL));
		pnEditOpts.add(lbDisplay);
		pnEditOpts.add(ckDispIn);
		pnEditOpts.add(ckDispOut);
		pnEditOpts.add(new JSeparator(SwingConstants.VERTICAL));
		pnEditOpts.add(btUndo);
		
		pnGameOpts.add(btPause);
		
		pnOptsMenu.add(pnUnivOpts, BorderLayout.NORTH);
		pnOptsMenu.add(pnGameOpts, BorderLayout.SOUTH);
		
		gameboardComponent = new R2GameBoardComponent();
		pnGameCore.add(gameboardComponent, BorderLayout.CENTER);
		pnGameCore.add(lbStatus, BorderLayout.SOUTH);
		
		pnGameCore.setPreferredSize(new Dimension(400,420));
		pnGameCore.setBorder(new LineBorder(Color.GRAY));
		pnGPalette.add(lbGPalette);
		pnGPalette.setBorder(new LineBorder(Color.GRAY));
		pnGPalette.setPreferredSize(new Dimension(50,400));
		
		//fill in the main panels
		mainpane.add(pnOptsMenu, BorderLayout.NORTH);
		mainpane.add(pnGameCore, BorderLayout.CENTER);
		mainpane.add(pnGPalette, BorderLayout.WEST);
		mainpane.add(pnProperty, BorderLayout.SOUTH);
		
		timer = new Timer(50, new ActionListener() {
		public void actionPerformed(ActionEvent event) {
			gameboardComponent.getGameBoard().tick(.05);
			gameboardComponent.repaint();		
		}
		});
	
		this.pack();
		
		chooser = new JFileChooser(".");
	}
	
	/**
	 * Set the text in the status bar.
	 * 
	 * @param text the String to place in the status bar
	 */
	public void setStatus(String text) {
		lbStatus.setText(text);
	}
	
	public void startGame() {
		gameboardComponent.setFocusable(true);
		gameboardComponent.requestFocusInWindow();
		timer.start();
	}
	
	public void freezeGame() {
		timer.stop();
	}
	
	/*
	 * -BEGIN LISTENERS-
	 *  Edit these to affect what components do.  May have
	 *  multiple entries (e.g. the SaveListener might be
	 *  fired when the button is clicked or when return is
	 *  pressed in the text field (not that we have a field,
	 *  but if we did)).
	 *
	 */
	private class LoadListener implements ActionListener {		
		public void actionPerformed(ActionEvent arg0) {
			setStatus("Load clicked");
			int returnVal = chooser.showOpenDialog(GizmoballFrame.this);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				File file = chooser.getSelectedFile();
				gameboardComponent.getCurrentInteractionMode().load(file);
			}
			gameboardComponent.repaint();
		}
	}

	private class SaveListener implements ActionListener {		
		public void actionPerformed(ActionEvent arg0) {
			setStatus("Save clicked");
			int returnVal = chooser.showOpenDialog(GizmoballFrame.this);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				File file = chooser.getSelectedFile();
				gameboardComponent.getCurrentInteractionMode().save(file);
			}
			
		}
	}
	
	private class ModeListener implements ActionListener {		
		public void actionPerformed(ActionEvent arg0) {
			// TODO hook up mode functionality
			setStatus("Mode clicked");
		}
	}
		
	private class QuitListener implements ActionListener {		
		public void actionPerformed(ActionEvent arg0) {
			// TODO add graceful unload features?
			System.exit(0);
		}
	}

	private class UndoListener implements ActionListener {		
		public void actionPerformed(ActionEvent arg0) {
			// TODO hook up undo functionality
			setStatus("Undo clicked");
			gameboardComponent.getCurrentInteractionMode().undo();
		}
	}

	private class SnapListener implements ActionListener {
		public void actionPerformed(ActionEvent arg0) {
			// TODO hook up snap check box
			setStatus("Snap to grid clicked: " + ckSnapToGrid.isSelected());
			gameboardComponent.getCurrentInteractionMode().setSnapToGrid(
				ckSnapToGrid.isSelected());
		}
	}

	private class CkInListener implements ActionListener {
		public void actionPerformed(ActionEvent arg0) {
			// TODO hook up incoming check box
			setStatus("Incoming clicked: " + ckDispIn.isSelected());
			gameboardComponent.getCurrentInteractionMode().setShowIncomingConnections(
				ckDispIn.isSelected());
		}
	}

	private class CkOutListener implements ActionListener {
		public void actionPerformed(ActionEvent arg0) {
			// TODO hook up outgoing check box
			setStatus("Outgoing clicked: " + ckDispOut.isSelected());
			gameboardComponent.getCurrentInteractionMode().setShowOutgoingConnections(
				ckDispOut.isSelected());
		}
	}

	private class PauseListener implements ActionListener {
		public void actionPerformed(ActionEvent arg0) {
			if (btPause.getText().equals("Start")) {
				btPause.setText("Pause");
				GizmoballFrame.this.setStatus("Game started");
				GizmoballFrame.this.startGame();
			}
			else {
				btPause.setText("Start");
				GizmoballFrame.this.setStatus("Game paused");
				GizmoballFrame.this.freezeGame();
			}
		}
	}

}
