import javax.swing.*;
import java.awt.*;
import java.awt.event.*;


/**
 * This class is a starter file for a "Tron LightCycle" game in which two players drive lightcycles in
 * an arena, leaving trails behind them as they go.  If a player bumps into one of these trails or
 * into a wall, the other player wins.  This game does not try to show the lightcycles themselves,
 * only the trails that are left.  In this starter program, there is just one lightcycle that leaves a
 * trail of random colors, and crossing a trail or hitting a wall has no effect.
 */
public class TronPanel extends JPanel implements FocusListener, KeyListener, ActionListener, MouseListener {

	private MosaicPanel arena;  // The arena in which the "lightcycles" move.  It consists of squares arranged
	                            // into rows and columns.
	
	private JLabel message;   // This label appears under the arena and holds a message for the user.

	private Timer timer;  // Whenever the arena has the input focus, this timer emits a stream of ActionEvents
	                      // that drive the action.  Everytime an ActionEvent occurs, the actionPerformed method
	                      // in this class is called.
	
	private final static int ROWS = 60;         // Number of rows in the arena.
	private final static int COLUMNS = 80;      // Number of columns in the arena.
	private final static int BLOCKSIZE = 10;    // Size of each square in the arena-- used only in creating the arena object.
	private final static int BORDER_WIDTH = 5;  // The width of the colored border around the arena.
	
	private final static int UP = 0, LEFT = 1, DOWN = 2, RIGHT = 3, NOT_MOVING = 4; // Constants representing possible directions.
	private int direction;  // The direction that the "lightcycle" is currently heading, one of the above constants.

	private int currentColumn, currentRow; // The current row number and column number where the "lightcycle" is located.

	
	/**
	 * The "constructor" for the TronPanel class creates all the components that are
	 * displayed in the panel and adds them to the panel.  It also sets up listening
	 * for events.  The panel listens for mouse events from the label and from the
	 * arena, for focus events from the arena, for action events from the timer, and
	 * for key events from the arena.  (Thus, key events are only processed when the
	 * arena has the input focus.)
	 */
	public TronPanel() {
		arena = new MosaicPanel(ROWS, COLUMNS, BLOCKSIZE, BLOCKSIZE, Color.GRAY, BORDER_WIDTH);
		message = new JLabel("To Start, Click the Arena", JLabel.CENTER);
		message.setBackground(Color.LIGHT_GRAY);
		message.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
		JPanel bottom = new JPanel();
		bottom.setLayout(new BorderLayout());
		bottom.setBackground(Color.LIGHT_GRAY);
		setBackground(Color.DARK_GRAY);
		setLayout(new BorderLayout(3,3));
		bottom.add(message,BorderLayout.CENTER);
		add(bottom, BorderLayout.SOUTH);
		add(arena, BorderLayout.CENTER);
		setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY,3));
		arena.setGroutingColor(null);
		arena.addFocusListener(this);
		arena.addKeyListener(this);
		arena.addMouseListener(this);
		message.addMouseListener(this);
	}

	/**
	 * This method responds to the action events that are generated by the timer as
	 * long as the game is in progress.  It simply moves the "lightcycle" one square
	 * in the curretnly selected direction.  (It gets "stuck" in the same position
	 * when it hits one of the walls of the arena.)  The square containing the
	 * "lightcycle" is colored with a random color.  Note that if the current
	 * direction is NOT_MOVING, then the light cycle does not move.
	 */
	public void actionPerformed(ActionEvent e) {
		switch (direction) {
		case UP:
			if (currentRow > 0)
				currentRow--;    // Move up one row, unless already in the top row, number 0.
			break;
		case DOWN:
			if (currentRow < ROWS-1)
				currentRow++;   // Move down one row, unless already in the bottom row, number ROWS-1.
			break;
		case RIGHT:
			if (currentColumn < COLUMNS-1)
				currentColumn++;  // Move right one column, unless already in the rightmost column, number COLUMNS-1.
			break;
		case LEFT:
			if (currentColumn > 0)
				currentColumn--;  // Move left one column, unless already in the leftmost column, number 0.
			break;
		}
		arena.setHSBColor(currentRow,currentColumn,Math.random(),1,1);
	}

	/**
	 * This method is called when the user presses a key on the keyboard (but only if the
	 * arena has the input focus).  If the user presses an arrow key, the current direction
	 * of motion of the light cycle is set to the direction that corresponds to the arrow
	 * (up, down, left, or right).  If the "P" key is pressed, the game is paused (by giving
	 * the input focus to the message component, which indirectly stops the game from running).
s	 */
	public void keyPressed(KeyEvent e) {
		int code = e.getKeyCode();  // This code tells which key was pressed.  The value is one of the 
		                            // virtual keyboard ("VK") constants in the KeyEvent class.
		if (code == KeyEvent.VK_LEFT)
			direction = LEFT;
		else if (code == KeyEvent.VK_RIGHT)
			direction = RIGHT;
		else if (code == KeyEvent.VK_UP)
			direction = UP;
		else if (code == KeyEvent.VK_DOWN)
			direction = DOWN;
		else if (code == KeyEvent.VK_P)
			message.requestFocus();
	}

	/**
	 * This method is called when the arena gains focus, which means that it will start
	 * receiving Key events.  When this happens, the game action is turned on (by creating
	 * a timer to drive the game), the color of the arena's border is changed to cyan,
	 * and the text of the message is changed.
	 */
	public void focusGained(FocusEvent e) {
		arena.setBorder(BorderFactory.createLineBorder(Color.CYAN, BORDER_WIDTH));
		arena.fill(null);  // This resets all the squares to black to erase the picture from the previous run.
		currentColumn = COLUMNS/4*3;  // Starting column, 3/4 of the way across the scree.
		currentRow = ROWS/2;          // Starting row, halfway down the screen
		direction = UP;               // Starting direction of motion.
		arena.setColor(currentRow,currentColumn,255,0,0);  // color first square red
		direction = NOT_MOVING;       // No motion until a direction key is pressed
		message.setText("To PAUSE, Click this Message or Press \"P\"");
		timer = new Timer(50,this); // timer generates an ActionEvent every 50 milliseconds
		timer.start();
	}

	/**
	 * This method is called when the arena loses focus, which means that it will not
	 * receive Key events.  When this happens, the game is suspended (by turning off 
	 * the timer that drives the game), the color of the arena's border is changed to
	 * gray, and the text of the message is changed.
	 */
	public void focusLost(FocusEvent e) {
		arena.setBorder(BorderFactory.createLineBorder(Color.GRAY, BORDER_WIDTH));
		if (timer != null)
			timer.stop();
		timer = null;
		message.setText("To START, Click the Arena");
	}

	/**
	 * Mouse clicks on the arena and on the message are simply used to move the focus to
	 * the component that was clicked.  (The point of giving the focus to the message is
	 * simply to make the arena lose focus, which causes the game to be paused.)
	 */
	public void mousePressed(MouseEvent e) {
		if (e.getSource() == arena)
			arena.requestFocus();
		else
			message.requestFocus();
	}

	
	// The following subroutines are required by the "KeyListener" and "MouseListener" interfaces,
	// but are not used in this program.  They are defined as subrotines with empty bodies, which
	// means that they do nothing.
	
	public void mouseClicked(MouseEvent e) {}
	public void mouseEntered(MouseEvent e) {}
	public void mouseExited(MouseEvent e) {}
	public void mouseReleased(MouseEvent e) {}
	public void keyReleased(KeyEvent e) {}
	public void keyTyped(KeyEvent e) {}



} // end class TronPanel

