
/*
    Copyright (C) 2005  University Of New South Wales

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*
 * GUI.java
 *
 * Created on 9 January 2005, 00:12
 */
import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.util.*;
import javax.swing.*;

/**
 * MainGUI is the starting class of the program. The GUI is shown and it
 * interacts with a RoboCupGameControlData and starts a Broadcast on a new
 * thread to broadcast the RoboCupGameControlData structure.
 *
 * The majority of the GUI code is generated by NetBeans 4.
 *
 * @author willu@cse.unsw.edu.au shnl327@cse.unsw.edu.au
 */
public class MainGUI extends javax.swing.JFrame {
    
    // create a RoboCupGameControlData for use
    private static RoboCupGameControlData data;
    
    // instance of the GameController to use for the GUI
    // the GameController thread is started in main
    private static Broadcast broadcast;

	// The colour of the kickoff team
	// -1 = not set, Constants.TEAM_BLUE or Constants.TEAM_RED
	private int halfKickoffTeam = -1;

	// For logging
	private static java.io.PrintWriter log = null;

    /** Creates new form GUI */
    // team numbers are given by GameController to initialise data structure
    // and broadcast address to initialise the Broadcast class
    public MainGUI(int blueNumber, int redNumber, String broadcastAddr, int port) {
        
        // create the GameController and RoboCupGameControlData based on the 
        // command line arguments
        data      = new RoboCupGameControlData(blueNumber, redNumber);
        broadcast = new Broadcast(data, broadcastAddr, port);

        // start the GameController thread
        Thread broadcastThread = new Thread(broadcast);
        broadcastThread.start();
        
        // open the log for appending
        try {
        	if (log == null)
	        	log = new java.io.PrintWriter(new java.io.FileWriter(Constants.LOG_FILENAME, true));
        } catch (java.io.IOException e) {
        	throw new RuntimeException(e.toString());
        }
        
        initComponents();

        // start timer object
        new javax.swing.Timer(oneSecond, clock).start();

        cmdInvisible.setVisible(false);
        lblTime.setText(mins + ":00");     // initialise clockface
        
        // initialse team panels with team numbers
        pnlBlue.setBorder(new javax.swing.border.TitledBorder(null, 
                          "Blue Team - #" + data.getTeamNumber(Constants.TEAM_BLUE),
                          javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION,
                          javax.swing.border.TitledBorder.DEFAULT_POSITION,
                          new java.awt.Font("Dialog", 1, 12)));
        pnlRed.setBorder(new javax.swing.border.TitledBorder(null, 
                         "Red Team - #" + data.getTeamNumber(Constants.TEAM_RED),
                         javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION,
                         javax.swing.border.TitledBorder.DEFAULT_POSITION,
                         new java.awt.Font("Dialog", 1, 12)));
        
        logString("Starting Gamecontroller : " + 
        		"Red team is #" + data.getTeamNumber(Constants.TEAM_RED) +
        		", Blue team is #" + data.getTeamNumber(Constants.TEAM_BLUE));
    }
    

	private static void logString(String s) {
		log.print((new java.util.Date()));
		log.print(" : ");
		log.println(s);
		log.flush();
	}
	
	private static String getGameTimeString() {
		return ("" + mins + ":" + ((secs < 10)?"0":"") + secs);
	}
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    private void initComponents() {//GEN-BEGIN:initComponents
        gameButtons = new javax.swing.ButtonGroup();
        kickOffButtons = new javax.swing.ButtonGroup();
        penaltyButtons = new javax.swing.ButtonGroup();
        pnlMain = new javax.swing.JPanel();
        cmdBlueDropIn = new javax.swing.JButton();
        cmdInitial = new javax.swing.JToggleButton();
        cmdReady = new javax.swing.JToggleButton();
        cmdKickOff = new javax.swing.JToggleButton();
        cmdSet = new javax.swing.JToggleButton();
        cmdFinish = new javax.swing.JToggleButton();
        cmdRedDropIn = new javax.swing.JButton();
        chkAutoPause = new javax.swing.JCheckBox();
        pnlPenalties = new javax.swing.JPanel();
        cmdBallHolding = new javax.swing.JToggleButton();
        cmdGoaliePushing = new javax.swing.JToggleButton();
        cmdPlayerPushing = new javax.swing.JToggleButton();
        cmdDamage = new javax.swing.JToggleButton();
        cmdLeavingField = new javax.swing.JToggleButton();
        cmdIllegalDefender = new javax.swing.JToggleButton();
        cmdIllegalDefense = new javax.swing.JToggleButton();
        cmdObstruction = new javax.swing.JToggleButton();
        cmdInvisible = new javax.swing.JToggleButton();
        cmdPickupRequest = new javax.swing.JToggleButton();
        pnlStatus = new javax.swing.JPanel();
        pnlBlue = new javax.swing.JPanel();
        cmdBlue1Unpenalise = new javax.swing.JButton();
        cmdBlue2Unpenalise = new javax.swing.JButton();
        cmdBlue3Unpenalise = new javax.swing.JButton();
        cmdBlue4Unpenalise = new javax.swing.JButton();
        cmdBlue1 = new javax.swing.JToggleButton();
        cmdBlue2 = new javax.swing.JToggleButton();
        cmdBlue3 = new javax.swing.JToggleButton();
        cmdBlue4 = new javax.swing.JToggleButton();
        pnlRed = new javax.swing.JPanel();
        cmdRed3Unpenalise = new javax.swing.JButton();
        cmdRed4Unpenalise = new javax.swing.JButton();
        cmdRed1Unpenalise = new javax.swing.JButton();
        cmdRed2Unpenalise = new javax.swing.JButton();
        cmdRed1 = new javax.swing.JToggleButton();
        cmdRed2 = new javax.swing.JToggleButton();
        cmdRed3 = new javax.swing.JToggleButton();
        cmdRed4 = new javax.swing.JToggleButton();
        pnlScore = new javax.swing.JPanel();
        lblBlueTeam = new javax.swing.JLabel();
        lblRedTeam = new javax.swing.JLabel();
        lblBlueScore = new javax.swing.JLabel();
        lblRedScore = new javax.swing.JLabel();
        cmdBlueScoreDown = new javax.swing.JButton();
        cmdBlueScoreUp = new javax.swing.JButton();
        cmdRedScoreDown = new javax.swing.JButton();
        cmdRedScoreUp = new javax.swing.JButton();
        pnlTime = new javax.swing.JPanel();
        cmdTimeStart = new javax.swing.JButton();
        cmdTimeReset = new javax.swing.JButton();
        cmbHalf = new javax.swing.JComboBox();
        lblTime = new javax.swing.JTextField();
        pnlKickOff = new javax.swing.JPanel();
        cmdBlueKickOff = new javax.swing.JToggleButton();
        cmdRedKickOff = new javax.swing.JToggleButton();

        getContentPane().setLayout(null);

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("RoboCup GameController 2005 (ver. " + Constants.GUI_VERSION + ")");
        setName("frmMain");
        setResizable(false);
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                close(evt);
            }
        });

        pnlMain.setLayout(null);

        pnlMain.setBorder(new javax.swing.border.TitledBorder(null, "Game", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 0, 12)));
        cmdBlueDropIn.setBackground(new java.awt.Color(153, 153, 255));
        cmdBlueDropIn.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdBlueDropIn.setText("Out By Blue");
        cmdBlueDropIn.setToolTipText("Click when the Blue Team kicks the ball out");
        cmdBlueDropIn.setMargin(new java.awt.Insets(2, 1, 2, 1));
        cmdBlueDropIn.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdBlueDropIn_clicked(evt);
            }
        });

        pnlMain.add(cmdBlueDropIn);
        cmdBlueDropIn.setBounds(10, 140, 88, 25);

        gameButtons.add(cmdInitial);
        cmdInitial.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdInitial.setSelected(true);
        cmdInitial.setText("Initial");
        cmdInitial.setToolTipText("Click this button to set the game to the \"Initial\" state.");
        cmdInitial.setMargin(new java.awt.Insets(2, 5, 2, 5));
        cmdInitial.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdInitial_clicked(evt);
            }
        });

        pnlMain.add(cmdInitial);
        cmdInitial.setBounds(10, 20, 90, 25);

        gameButtons.add(cmdReady);
        cmdReady.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdReady.setText("Ready");
        cmdReady.setToolTipText("Click this button to set the game to the \"Ready\" state.");
        cmdReady.setMargin(new java.awt.Insets(2, 5, 2, 5));
        cmdReady.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdReady_clicked(evt);
            }
        });

        pnlMain.add(cmdReady);
        cmdReady.setBounds(10, 50, 90, 25);

        gameButtons.add(cmdKickOff);
        cmdKickOff.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdKickOff.setText("Play");
        cmdKickOff.setToolTipText("Click this button to set the game to the \"Playing\" state.");
        cmdKickOff.setMargin(new java.awt.Insets(2, 5, 2, 5));
        cmdKickOff.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdKickOff_clicked(evt);
            }
        });

        pnlMain.add(cmdKickOff);
        cmdKickOff.setBounds(10, 110, 90, 25);

        gameButtons.add(cmdSet);
        cmdSet.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdSet.setText("Set");
        cmdSet.setToolTipText("Click this button to set the game to the \"Set\" state.");
        cmdSet.setMargin(new java.awt.Insets(2, 5, 2, 5));
        cmdSet.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdSet_clicked(evt);
            }
        });

        pnlMain.add(cmdSet);
        cmdSet.setBounds(10, 80, 90, 25);

        gameButtons.add(cmdFinish);
        cmdFinish.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdFinish.setText("Finish");
        cmdFinish.setToolTipText("Click this button to set the game to the \"Finish\" state.");
        cmdFinish.setMargin(new java.awt.Insets(2, 5, 2, 5));
        cmdFinish.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdFinish_clicked(evt);
            }
        });

        pnlMain.add(cmdFinish);
        cmdFinish.setBounds(10, 200, 90, 25);

        cmdRedDropIn.setBackground(new java.awt.Color(255, 102, 102));
        cmdRedDropIn.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdRedDropIn.setText("Out By Red");
        cmdRedDropIn.setToolTipText("Click when the Red Team kicks the ball out");
        cmdRedDropIn.setMargin(new java.awt.Insets(2, 1, 2, 1));
        cmdRedDropIn.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdRedDropIn_clicked(evt);
            }
        });

        pnlMain.add(cmdRedDropIn);
        cmdRedDropIn.setBounds(10, 170, 88, 25);

        chkAutoPause.setFont(new java.awt.Font("Dialog", 0, 12));
        chkAutoPause.setText("Auto Pause");
        chkAutoPause.setToolTipText("Check this option to automatically pause the clock when in \"Ready\" and \"Set\" states.");
        pnlMain.add(chkAutoPause);
        chkAutoPause.setBounds(10, 230, 90, 23);

        getContentPane().add(pnlMain);
        pnlMain.setBounds(10, 100, 110, 270);

        pnlPenalties.setLayout(null);

        pnlPenalties.setBorder(new javax.swing.border.TitledBorder(null, "Penalties", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 0, 12)));
        penaltyButtons.add(cmdBallHolding);
        cmdBallHolding.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdBallHolding.setText("Ball Holding");
        cmdBallHolding.setToolTipText("Sanction a \"Ball Holding\" penalty to the selected players.");
        cmdBallHolding.setMargin(new java.awt.Insets(2, 5, 2, 5));
        cmdBallHolding.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                penalty_clicked(evt);
            }
        });

        pnlPenalties.add(cmdBallHolding);
        cmdBallHolding.setBounds(10, 30, 130, 25);

        penaltyButtons.add(cmdGoaliePushing);
        cmdGoaliePushing.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdGoaliePushing.setText("Goalie Pushing");
        cmdGoaliePushing.setToolTipText("Sanction a \"Goalie Pushing\" penalty to the selected players.");
        cmdGoaliePushing.setMargin(new java.awt.Insets(2, 5, 2, 5));
        cmdGoaliePushing.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                penalty_clicked(evt);
            }
        });

        pnlPenalties.add(cmdGoaliePushing);
        cmdGoaliePushing.setBounds(10, 60, 130, 25);

        penaltyButtons.add(cmdPlayerPushing);
        cmdPlayerPushing.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdPlayerPushing.setText("Player Pushing");
        cmdPlayerPushing.setToolTipText("Sanction a \"Player Pushing\" penalty to the selected players.");
        cmdPlayerPushing.setMargin(new java.awt.Insets(2, 5, 2, 5));
        cmdPlayerPushing.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                penalty_clicked(evt);
            }
        });

        pnlPenalties.add(cmdPlayerPushing);
        cmdPlayerPushing.setBounds(10, 90, 130, 25);

        penaltyButtons.add(cmdDamage);
        cmdDamage.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdDamage.setText("Damage");
        cmdDamage.setToolTipText("Sanction a \"Damage\" penalty to the selected players.");
        cmdDamage.setMargin(new java.awt.Insets(2, 5, 2, 5));
        cmdDamage.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                penalty_clicked(evt);
            }
        });

        pnlPenalties.add(cmdDamage);
        cmdDamage.setBounds(10, 120, 130, 25);

        penaltyButtons.add(cmdLeavingField);
        cmdLeavingField.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdLeavingField.setText("Leaving Field");
        cmdLeavingField.setToolTipText("Sanction a \"Leaving the Field\" penalty to the selected players.");
        cmdLeavingField.setMargin(new java.awt.Insets(2, 5, 2, 5));
        cmdLeavingField.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                penalty_clicked(evt);
            }
        });

        pnlPenalties.add(cmdLeavingField);
        cmdLeavingField.setBounds(10, 150, 130, 25);

        penaltyButtons.add(cmdIllegalDefender);
        cmdIllegalDefender.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdIllegalDefender.setText("Illegal Defender");
        cmdIllegalDefender.setToolTipText("Sanction a \"Illegal Defender\" penalty to the selected players.");
        cmdIllegalDefender.setMargin(new java.awt.Insets(2, 5, 2, 5));
        cmdIllegalDefender.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                penalty_clicked(evt);
            }
        });

        pnlPenalties.add(cmdIllegalDefender);
        cmdIllegalDefender.setBounds(10, 180, 130, 25);

        penaltyButtons.add(cmdIllegalDefense);
        cmdIllegalDefense.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdIllegalDefense.setText("Illegal Defense");
        cmdIllegalDefense.setToolTipText("Sanction a \"Illegal Defense\" penalty to the selected players.");
        cmdIllegalDefense.setMargin(new java.awt.Insets(2, 5, 2, 5));
        cmdIllegalDefense.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                penalty_clicked(evt);
            }
        });

        pnlPenalties.add(cmdIllegalDefense);
        cmdIllegalDefense.setBounds(10, 210, 130, 25);

        penaltyButtons.add(cmdObstruction);
        cmdObstruction.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdObstruction.setText("Obstruction");
        cmdObstruction.setToolTipText("Sanction a \"Obstruction\" penalty to the selected players.");
        cmdObstruction.setMargin(new java.awt.Insets(2, 5, 2, 5));
        cmdObstruction.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                penalty_clicked(evt);
            }
        });

        pnlPenalties.add(cmdObstruction);
        cmdObstruction.setBounds(10, 240, 130, 25);

        penaltyButtons.add(cmdInvisible);
        cmdInvisible.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdInvisible.setSelected(true);
        cmdInvisible.setText("Invisible");
        cmdInvisible.setToolTipText("This is here to simulate none selected in the button group");
        cmdInvisible.setMargin(new java.awt.Insets(2, 5, 2, 5));
        pnlPenalties.add(cmdInvisible);
        cmdInvisible.setBounds(10, 300, 130, 25);

        penaltyButtons.add(cmdPickupRequest);
        cmdPickupRequest.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdPickupRequest.setText("Pick Up Request");
        cmdPickupRequest.setToolTipText("Sanction a \"Pick Up Request\" penalty to the selected players.");
        cmdPickupRequest.setMargin(new java.awt.Insets(2, 5, 2, 5));
        cmdPickupRequest.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                penalty_clicked(evt);
            }
        });

        pnlPenalties.add(cmdPickupRequest);
        cmdPickupRequest.setBounds(10, 270, 130, 25);

        getContentPane().add(pnlPenalties);
        pnlPenalties.setBounds(130, 10, 150, 360);

        pnlStatus.setLayout(null);

        pnlStatus.setBorder(new javax.swing.border.TitledBorder(null, "Status", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 0, 12)));
        pnlBlue.setLayout(null);

        pnlBlue.setBackground(new java.awt.Color(153, 153, 255));
        pnlBlue.setBorder(new javax.swing.border.TitledBorder(null, "Blue Team", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 0, 12)));
        cmdBlue1Unpenalise.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdBlue1Unpenalise.setText("No penalty");
        cmdBlue1Unpenalise.setToolTipText("If penalised, click here to unpenalise player.");
        cmdBlue1Unpenalise.setEnabled(false);
        cmdBlue1Unpenalise.setMargin(new java.awt.Insets(2, 2, 2, 2));
        cmdBlue1Unpenalise.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdBlue1UnpenaliseMouseClicked(evt);
            }
        });

        pnlBlue.add(cmdBlue1Unpenalise);
        cmdBlue1Unpenalise.setBounds(100, 30, 110, 25);

        cmdBlue2Unpenalise.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdBlue2Unpenalise.setText("No penalty");
        cmdBlue2Unpenalise.setToolTipText("If penalised, click here to unpenalise player.");
        cmdBlue2Unpenalise.setEnabled(false);
        cmdBlue2Unpenalise.setMargin(new java.awt.Insets(2, 2, 2, 2));
        cmdBlue2Unpenalise.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdBlue2UnpenaliseMouseClicked(evt);
            }
        });

        pnlBlue.add(cmdBlue2Unpenalise);
        cmdBlue2Unpenalise.setBounds(100, 60, 110, 25);

        cmdBlue3Unpenalise.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdBlue3Unpenalise.setText("No penalty");
        cmdBlue3Unpenalise.setToolTipText("If penalised, click here to unpenalise player.");
        cmdBlue3Unpenalise.setEnabled(false);
        cmdBlue3Unpenalise.setMargin(new java.awt.Insets(2, 2, 2, 2));
        cmdBlue3Unpenalise.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdBlue3UnpenaliseMouseClicked(evt);
            }
        });

        pnlBlue.add(cmdBlue3Unpenalise);
        cmdBlue3Unpenalise.setBounds(100, 90, 110, 25);

        cmdBlue4Unpenalise.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdBlue4Unpenalise.setText("No penalty");
        cmdBlue4Unpenalise.setToolTipText("If penalised, click here to unpenalise player.");
        cmdBlue4Unpenalise.setEnabled(false);
        cmdBlue4Unpenalise.setMargin(new java.awt.Insets(2, 2, 2, 2));
        cmdBlue4Unpenalise.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdBlue4UnpenaliseMouseClicked(evt);
            }
        });

        pnlBlue.add(cmdBlue4Unpenalise);
        cmdBlue4Unpenalise.setBounds(100, 120, 110, 25);

        cmdBlue1.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdBlue1.setText("Player 1");
        cmdBlue1.setToolTipText("Click to selected Blue robot 1");
        cmdBlue1.setMargin(new java.awt.Insets(2, 7, 2, 7));
        cmdBlue1.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdBlue_clicked(evt);
            }
        });

        pnlBlue.add(cmdBlue1);
        cmdBlue1.setBounds(10, 30, 80, 25);

        cmdBlue2.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdBlue2.setText("Player 2");
        cmdBlue2.setToolTipText("Click to selected Blue robot 2");
        cmdBlue2.setMargin(new java.awt.Insets(2, 7, 2, 7));
        cmdBlue2.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdBlue_clicked(evt);
            }
        });

        pnlBlue.add(cmdBlue2);
        cmdBlue2.setBounds(10, 60, 80, 25);

        cmdBlue3.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdBlue3.setText("Player 3");
        cmdBlue3.setToolTipText("Click to selected Blue robot 3");
        cmdBlue3.setMargin(new java.awt.Insets(2, 7, 2, 7));
        cmdBlue3.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdBlue_clicked(evt);
            }
        });

        pnlBlue.add(cmdBlue3);
        cmdBlue3.setBounds(10, 90, 80, 25);

        cmdBlue4.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdBlue4.setText("Player 4");
        cmdBlue4.setToolTipText("Click to selected Blue robot 4");
        cmdBlue4.setMargin(new java.awt.Insets(2, 7, 2, 7));
        cmdBlue4.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdBlue_clicked(evt);
            }
        });

        pnlBlue.add(cmdBlue4);
        cmdBlue4.setBounds(10, 120, 80, 25);

        pnlStatus.add(pnlBlue);
        pnlBlue.setBounds(10, 20, 220, 160);

        pnlRed.setLayout(null);

        pnlRed.setBackground(new java.awt.Color(255, 102, 102));
        pnlRed.setBorder(new javax.swing.border.TitledBorder(null, "Red Team", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 0, 12)));
        cmdRed3Unpenalise.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdRed3Unpenalise.setText("No penalty");
        cmdRed3Unpenalise.setToolTipText("If penalised, click here to unpenalise player.");
        cmdRed3Unpenalise.setEnabled(false);
        cmdRed3Unpenalise.setMargin(new java.awt.Insets(2, 2, 2, 2));
        cmdRed3Unpenalise.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdRed3UnpenaliseMouseClicked(evt);
            }
        });

        pnlRed.add(cmdRed3Unpenalise);
        cmdRed3Unpenalise.setBounds(100, 90, 110, 25);

        cmdRed4Unpenalise.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdRed4Unpenalise.setText("No penalty");
        cmdRed4Unpenalise.setToolTipText("If penalised, click here to unpenalise player.");
        cmdRed4Unpenalise.setEnabled(false);
        cmdRed4Unpenalise.setMargin(new java.awt.Insets(2, 2, 2, 2));
        cmdRed4Unpenalise.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdRed4UnpenaliseMouseClicked(evt);
            }
        });

        pnlRed.add(cmdRed4Unpenalise);
        cmdRed4Unpenalise.setBounds(100, 120, 110, 25);

        cmdRed1Unpenalise.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdRed1Unpenalise.setText("No penalty");
        cmdRed1Unpenalise.setToolTipText("If penalised, click here to unpenalise player.");
        cmdRed1Unpenalise.setEnabled(false);
        cmdRed1Unpenalise.setMargin(new java.awt.Insets(2, 2, 2, 2));
        cmdRed1Unpenalise.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdRed1UnpenaliseMouseClicked(evt);
            }
        });

        pnlRed.add(cmdRed1Unpenalise);
        cmdRed1Unpenalise.setBounds(100, 30, 110, 25);

        cmdRed2Unpenalise.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdRed2Unpenalise.setText("No penalty");
        cmdRed2Unpenalise.setToolTipText("If penalised, click here to unpenalise player.");
        cmdRed2Unpenalise.setEnabled(false);
        cmdRed2Unpenalise.setMargin(new java.awt.Insets(2, 2, 2, 2));
        cmdRed2Unpenalise.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdRed2UnpenaliseMouseClicked(evt);
            }
        });

        pnlRed.add(cmdRed2Unpenalise);
        cmdRed2Unpenalise.setBounds(100, 60, 110, 25);

        cmdRed1.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdRed1.setText("Player 1");
        cmdRed1.setToolTipText("Click to selected Red robot 1");
        cmdRed1.setMargin(new java.awt.Insets(2, 7, 2, 7));
        cmdRed1.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdRed_clicked(evt);
            }
        });

        pnlRed.add(cmdRed1);
        cmdRed1.setBounds(10, 30, 80, 25);

        cmdRed2.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdRed2.setText("Player 2");
        cmdRed2.setToolTipText("Click to selected Red robot 2");
        cmdRed2.setMargin(new java.awt.Insets(2, 7, 2, 7));
        cmdRed2.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdRed_clicked(evt);
            }
        });

        pnlRed.add(cmdRed2);
        cmdRed2.setBounds(10, 60, 80, 25);

        cmdRed3.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdRed3.setText("Player 3");
        cmdRed3.setToolTipText("Click to selected Red robot 3");
        cmdRed3.setMargin(new java.awt.Insets(2, 7, 2, 7));
        cmdRed3.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdRed_clicked(evt);
            }
        });

        pnlRed.add(cmdRed3);
        cmdRed3.setBounds(10, 90, 80, 25);

        cmdRed4.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdRed4.setText("Player 4");
        cmdRed4.setToolTipText("Click to selected Red robot 4");
        cmdRed4.setMargin(new java.awt.Insets(2, 7, 2, 7));
        cmdRed4.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdRed_clicked(evt);
            }
        });

        pnlRed.add(cmdRed4);
        cmdRed4.setBounds(10, 120, 80, 25);

        pnlStatus.add(pnlRed);
        pnlRed.setBounds(10, 190, 220, 160);

        pnlScore.setLayout(null);

        pnlScore.setBorder(new javax.swing.border.TitledBorder(null, "Score", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 0, 12)));
        lblBlueTeam.setFont(new java.awt.Font("Dialog", 1, 24));
        lblBlueTeam.setText("BLUE:");
        lblBlueTeam.setToolTipText("Blue team score.");
        pnlScore.add(lblBlueTeam);
        lblBlueTeam.setBounds(10, 30, 80, 40);

        lblRedTeam.setFont(new java.awt.Font("Dialog", 1, 24));
        lblRedTeam.setText("RED:");
        lblRedTeam.setToolTipText("Red team score.");
        pnlScore.add(lblRedTeam);
        lblRedTeam.setBounds(10, 90, 70, 40);

        lblBlueScore.setFont(new java.awt.Font("Dialog", 1, 24));
        lblBlueScore.setText("0");
        lblBlueScore.setToolTipText("Blue team score.");
        pnlScore.add(lblBlueScore);
        lblBlueScore.setBounds(90, 30, 40, 40);

        lblRedScore.setFont(new java.awt.Font("Dialog", 1, 24));
        lblRedScore.setText("0");
        lblRedScore.setToolTipText("Red team score.");
        pnlScore.add(lblRedScore);
        lblRedScore.setBounds(90, 90, 40, 40);

        cmdBlueScoreDown.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdBlueScoreDown.setText("-");
        cmdBlueScoreDown.setToolTipText("Click to decrement Blue score.");
        cmdBlueScoreDown.setMargin(new java.awt.Insets(2, 7, 2, 7));
        cmdBlueScoreDown.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdBlueScoreDown_clicked(evt);
            }
        });

        pnlScore.add(cmdBlueScoreDown);
        cmdBlueScoreDown.setBounds(170, 40, 30, 25);

        cmdBlueScoreUp.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdBlueScoreUp.setText("+");
        cmdBlueScoreUp.setToolTipText("Click to increment Blue score.");
        cmdBlueScoreUp.setMargin(new java.awt.Insets(2, 7, 2, 7));
        cmdBlueScoreUp.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdBlueScoreUp_clicked(evt);
            }
        });

        pnlScore.add(cmdBlueScoreUp);
        cmdBlueScoreUp.setBounds(130, 40, 30, 25);

        cmdRedScoreDown.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdRedScoreDown.setText("-");
        cmdRedScoreDown.setToolTipText("Click to decrement Red score.");
        cmdRedScoreDown.setMargin(new java.awt.Insets(2, 7, 2, 7));
        cmdRedScoreDown.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdRedScoreDown_clicked(evt);
            }
        });

        pnlScore.add(cmdRedScoreDown);
        cmdRedScoreDown.setBounds(170, 100, 30, 25);

        cmdRedScoreUp.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdRedScoreUp.setText("+");
        cmdRedScoreUp.setToolTipText("Click to increment Red score.");
        cmdRedScoreUp.setMargin(new java.awt.Insets(2, 7, 2, 7));
        cmdRedScoreUp.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdRedScoreUp_clicked(evt);
            }
        });

        pnlScore.add(cmdRedScoreUp);
        cmdRedScoreUp.setBounds(130, 100, 30, 25);

        pnlStatus.add(pnlScore);
        pnlScore.setBounds(240, 20, 220, 160);

        pnlTime.setLayout(null);

        pnlTime.setBorder(new javax.swing.border.TitledBorder(null, "Unofficial Time", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 0, 12)));
        pnlTime.setPreferredSize(new java.awt.Dimension(100, 110));
        cmdTimeStart.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdTimeStart.setText("Start / Stop");
        cmdTimeStart.setToolTipText("Start / Stop the timer.");
        cmdTimeStart.setMaximumSize(new java.awt.Dimension(70, 25));
        cmdTimeStart.setMinimumSize(new java.awt.Dimension(70, 25));
        cmdTimeStart.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdTimeStart_clicked(evt);
            }
        });

        pnlTime.add(cmdTimeStart);
        cmdTimeStart.setBounds(40, 70, 150, 25);

        cmdTimeReset.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdTimeReset.setText("Reset");
        cmdTimeReset.setToolTipText("Reset the timer.");
        cmdTimeReset.setMargin(new java.awt.Insets(2, 2, 2, 2));
        cmdTimeReset.setMaximumSize(new java.awt.Dimension(70, 25));
        cmdTimeReset.setMinimumSize(new java.awt.Dimension(70, 25));
        cmdTimeReset.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdTimeReset_clicked(evt);
            }
        });

        pnlTime.add(cmdTimeReset);
        cmdTimeReset.setBounds(40, 100, 150, 25);

        cmbHalf.setFont(new java.awt.Font("Dialog", 0, 12));
        cmbHalf.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "First Half", "Second Half" }));
        cmbHalf.setToolTipText("Select whether the game is in first half or second half. Changing this option will switch the teams around automatically.");
        cmbHalf.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                cmbHalfActionPerformed(evt);
            }
        });

        pnlTime.add(cmbHalf);
        cmbHalf.setBounds(40, 130, 150, 24);

        lblTime.setFont(new java.awt.Font("Dialog", 1, 36));
        lblTime.setHorizontalAlignment(javax.swing.JTextField.CENTER);
        lblTime.setText("00:00");
        lblTime.setToolTipText("Time remaining in half (unofficial). If you edit the time, press ENTER to commit the changes.");
        lblTime.setEnabled(false);
        lblTime.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                lblTimeKeyPressed(evt);
            }
        });

        pnlTime.add(lblTime);
        lblTime.setBounds(40, 20, 150, 40);

        pnlStatus.add(pnlTime);
        pnlTime.setBounds(240, 190, 220, 160);

        getContentPane().add(pnlStatus);
        pnlStatus.setBounds(290, 10, 470, 360);

        pnlKickOff.setBorder(new javax.swing.border.TitledBorder(null, "Kick Off Team", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 0, 12)));
        kickOffButtons.add(cmdBlueKickOff);
        cmdBlueKickOff.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdBlueKickOff.setSelected(true);
        cmdBlueKickOff.setText("Blue Kick Off");
        cmdBlueKickOff.setToolTipText("Click this button to select the Blue team for the next kick off.");
        cmdBlueKickOff.setMargin(new java.awt.Insets(2, 5, 2, 5));
        cmdBlueKickOff.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdBlueKickOff_clicked(evt);
            }
        });

        pnlKickOff.add(cmdBlueKickOff);

        kickOffButtons.add(cmdRedKickOff);
        cmdRedKickOff.setFont(new java.awt.Font("Dialog", 0, 12));
        cmdRedKickOff.setText("Red Kick Off");
        cmdRedKickOff.setToolTipText("Click this button to select the Red team for the next kick off.");
        cmdRedKickOff.setMargin(new java.awt.Insets(2, 5, 2, 5));
        cmdRedKickOff.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cmdRedKickOff_clicked(evt);
            }
        });

        pnlKickOff.add(cmdRedKickOff);

        getContentPane().add(pnlKickOff);
        pnlKickOff.setBounds(10, 10, 110, 90);

        java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
        setBounds((screenSize.width-773)/2, (screenSize.height-402)/2, 773, 402);
    }//GEN-END:initComponents

    
    // when changing the clock time, the time needs to be applied to the data 
    // structure and the GUI
    private void lblTimeKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_lblTimeKeyPressed
              
        if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
            if (Constants.debug) { System.out.println("commit time change"); }
            
            // just change the class members, change in the data structure will
            // automatically be done by updateEstimatedSecs
            mins = Integer.parseInt(lblTime.getText().split(":")[0]);
            secs = Integer.parseInt(lblTime.getText().split(":")[1]);
			logString("Manually changing remaining time to: " + getGameTimeString());
        }
        
    }//GEN-LAST:event_lblTimeKeyPressed
    
    
    // change the game half flag in the data structure    
    private void cmbHalfActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cmbHalfActionPerformed

        String half = (String)cmbHalf.getSelectedItem();
        boolean firstHalf = half.equals("First Half");
        
        if (firstHalf != data.getHalf()) {
        	data.setHalf(firstHalf);
        	switchTeams();
        	logString("Switching halves to " + half);
			if (halfKickoffTeam != -1) {
				data.setKickOffTeam(halfKickoffTeam);
				if (halfKickoffTeam == Constants.TEAM_BLUE) {
					cmdBlueKickOff.setSelected(true);
					logString("Auto setting half kickoff to Blue");
				} else {
					cmdRedKickOff.setSelected(true);
					logString("Auto setting half kickoff to Red");
				}
			}
			cmdTimeReset_clicked(null);
        }
    }//GEN-LAST:event_cmbHalfActionPerformed
    
   
    // need to change the team numbers, colours, and scores around when the 
    // halves are changed
    private void switchTeams() {
        
        int tempTeam  = data.getTeamNumber(Constants.TEAM_BLUE);
        int tempScore = data.getScore(Constants.TEAM_BLUE);
        
        // swap the teams in variables
        data.setTeamNumber(Constants.TEAM_BLUE, data.getTeamNumber(Constants.TEAM_RED));     
        data.setScore(Constants.TEAM_BLUE, data.getScore(Constants.TEAM_RED));
        data.setTeamNumber(Constants.TEAM_RED, tempTeam);
        data.setScore(Constants.TEAM_RED, tempScore);
        
        // change the team numbers in the panel borders
        pnlBlue.setBorder(new javax.swing.border.TitledBorder(null, 
                          "Blue Team - #" + data.getTeamNumber(Constants.TEAM_BLUE),
                          javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION,
                          javax.swing.border.TitledBorder.DEFAULT_POSITION,
                          new java.awt.Font("Dialog", 1, 12)));
        pnlRed.setBorder(new javax.swing.border.TitledBorder(null, 
                         "Red Team - #" + data.getTeamNumber(Constants.TEAM_RED),
                         javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION,
                         javax.swing.border.TitledBorder.DEFAULT_POSITION,
                         new java.awt.Font("Dialog", 1, 12)));        
        
        // change the scores
        lblRedScore.setText("" + data.getScore(Constants.TEAM_RED));
        lblBlueScore.setText("" + data.getScore(Constants.TEAM_BLUE));
        
        // reset the drop in time and team
        data.resetDropIn();
                
        logString("Switching team colours : " + 
        		"Red team is #" + data.getTeamNumber(Constants.TEAM_RED) +
        		", Blue team is #" + data.getTeamNumber(Constants.TEAM_BLUE));
    }            
    
    
    /***************************************************************************
    *
    *  Unpenalise buttons
    *
    ***************************************************************************/
    
    // unpenalise the specified player
    private void unpenalisePlayer(int team, int number) {
        if (team == Constants.TEAM_BLUE) {
            data.setPenalty(Constants.TEAM_BLUE, number-1, Constants.PENALTY_NONE);
            logString("Unpenalise blue #" + number);
        } else {
            data.setPenalty(Constants.TEAM_RED, number-1, Constants.PENALTY_NONE);
            logString("Unpenalise red #" + number);
        }
    }
    
    // the below methods are called by the listeners of the unpenalise buttons,
    // they call unpenalisePlayer to modify the data structure so that the 
    // relevant robot has their penalty state set to Constants.PENALTY_NONE
    private void cmdRed4UnpenaliseMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdRed4UnpenaliseMouseClicked
        if (Constants.debug) { System.out.println("Clicked on Red 4 unpenalise"); }
        GUIUnpenalise(Constants.TEAM_RED, 4);
        unpenalisePlayer(Constants.TEAM_RED, 4);
    }//GEN-LAST:event_cmdRed4UnpenaliseMouseClicked
    
    private void cmdRed3UnpenaliseMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdRed3UnpenaliseMouseClicked
        if (Constants.debug) { System.out.println("Clicked on Red 3 unpenalise"); }
        GUIUnpenalise(Constants.TEAM_RED, 3);
        unpenalisePlayer(Constants.TEAM_RED, 3);
    }//GEN-LAST:event_cmdRed3UnpenaliseMouseClicked
    
    private void cmdRed2UnpenaliseMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdRed2UnpenaliseMouseClicked
        if (Constants.debug) { System.out.println("Clicked on Red 2 unpenalise"); }
        GUIUnpenalise(Constants.TEAM_RED, 2);
        unpenalisePlayer(Constants.TEAM_RED, 2);
    }//GEN-LAST:event_cmdRed2UnpenaliseMouseClicked
    
    private void cmdRed1UnpenaliseMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdRed1UnpenaliseMouseClicked
        if (Constants.debug) { System.out.println("Clicked on Red 1 unpenalise"); }
        GUIUnpenalise(Constants.TEAM_RED, 1);
        unpenalisePlayer(Constants.TEAM_RED, 1);
    }//GEN-LAST:event_cmdRed1UnpenaliseMouseClicked
    
    private void cmdBlue4UnpenaliseMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdBlue4UnpenaliseMouseClicked
        if (Constants.debug) { System.out.println("Clicked on Blue 4 unpenalise"); }
        GUIUnpenalise(Constants.TEAM_BLUE, 4);
        unpenalisePlayer(Constants.TEAM_BLUE, 4);
    }//GEN-LAST:event_cmdBlue4UnpenaliseMouseClicked
    
    private void cmdBlue3UnpenaliseMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdBlue3UnpenaliseMouseClicked
        if (Constants.debug) { System.out.println("Clicked on Blue 3 unpenalise"); }
        GUIUnpenalise(Constants.TEAM_BLUE, 3);
        unpenalisePlayer(Constants.TEAM_BLUE, 3);
    }//GEN-LAST:event_cmdBlue3UnpenaliseMouseClicked
    
    private void cmdBlue2UnpenaliseMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdBlue2UnpenaliseMouseClicked
        if (Constants.debug) { System.out.println("Clicked on Blue 2 unpenalise"); }
        GUIUnpenalise(Constants.TEAM_BLUE, 2);
        unpenalisePlayer(Constants.TEAM_BLUE, 2);
    }//GEN-LAST:event_cmdBlue2UnpenaliseMouseClicked
    
    private void cmdBlue1UnpenaliseMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdBlue1UnpenaliseMouseClicked
        if (Constants.debug) { System.out.println("Clicked on Blue 1 unpenalise"); }
        GUIUnpenalise(Constants.TEAM_BLUE, 1);
        unpenalisePlayer(Constants.TEAM_BLUE, 1);
    }//GEN-LAST:event_cmdBlue1UnpenaliseMouseClicked
    
    
    // wrapper for GUIUnpenalised to unpenalise all GUI elements
    private void GUIUnpenaliseAll() {
        for (int team=0; team<2; team++) {
            for (int player=1; player<=4; player++) {
                GUIUnpenalise(team, player);
            }
        }
        data.resetPenalties(); 
    }
    
    // reverts the GUI penalty buttons back to an unpenalised state
    private void GUIUnpenalise(int team, int player) {
        if (team == Constants.TEAM_BLUE && player == 1) {
            cmdBlue1Unpenalise.setText("No penalty");
            cmdBlue1Unpenalise.setEnabled(false);
            cmdBlue1Unpenalise.setBackground(new Color(204,204,204));
        } else if (team == Constants.TEAM_BLUE && player == 2) {
            cmdBlue2Unpenalise.setText("No penalty");
            cmdBlue2Unpenalise.setEnabled(false);
            cmdBlue2Unpenalise.setBackground(new Color(204,204,204));            
        } else if (team == Constants.TEAM_BLUE && player == 3) {
            cmdBlue3Unpenalise.setText("No penalty");
            cmdBlue3Unpenalise.setEnabled(false);
            cmdBlue3Unpenalise.setBackground(new Color(204,204,204));            
        } else if (team == Constants.TEAM_BLUE && player == 4) {
            cmdBlue4Unpenalise.setText("No penalty");
            cmdBlue4Unpenalise.setEnabled(false);
            cmdBlue4Unpenalise.setBackground(new Color(204,204,204));            
        } else if (team == Constants.TEAM_RED && player == 1) {
            cmdRed1Unpenalise.setText("No penalty");
            cmdRed1Unpenalise.setEnabled(false);
            cmdRed1Unpenalise.setBackground(new Color(204,204,204));            
        } else if (team == Constants.TEAM_RED && player == 2) {
            cmdRed2Unpenalise.setText("No penalty");
            cmdRed2Unpenalise.setEnabled(false);
            cmdRed2Unpenalise.setBackground(new Color(204,204,204));            
        } else if (team == Constants.TEAM_RED && player == 3) {
            cmdRed3Unpenalise.setText("No penalty");
            cmdRed3Unpenalise.setEnabled(false);
            cmdRed3Unpenalise.setBackground(new Color(204,204,204));            
        } else if (team == Constants.TEAM_RED && player == 4) {
            cmdRed4Unpenalise.setText("No penalty");
            cmdRed4Unpenalise.setEnabled(false);
            cmdRed4Unpenalise.setBackground(new Color(204,204,204));                        
        }
    }
    
    // reset the clock
    private void cmdTimeReset_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdTimeReset_clicked
        this.secs = 0;
        this.mins = Constants.TIME_MINUTES;
        lblTime.setText(mins + ":00");
        lblTime.setForeground(Color.BLACK);
        this.countForward = false;
        logString("Game time reset to " + getGameTimeString());
    }//GEN-LAST:event_cmdTimeReset_clicked
    
    // start/pause the clock
    private void cmdTimeStart_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdTimeStart_clicked
        startClock = !startClock;
        cmbHalf.setEnabled(!startClock);      // only enable changing of halves when the clock is paused
		if (startClock) {
			logString("Unpausing clock at time " + getGameTimeString());
		} else {
			logString("Pausing clock at time " + getGameTimeString());
		}
    }//GEN-LAST:event_cmdTimeStart_clicked
    
    
    
    /***************************************************************************
     *
     *  Code to modify the scores
     *
     **************************************************************************/    
    // events for the up/down score buttons. Need to concat "" since setText
    // only accepts strings, not ints
    // 1) count up/down score
    // 2) set the score in the data structure
    // 3) update the score on the GUI
    // 4) change kick off teams in GUI
    // 5) change kick off teams in data structure
    // 6) change state to "ready" if necessary (in GUI and data structure)
    private void cmdRedScoreDown_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdRedScoreDown_clicked
        int redScore = data.getScore(Constants.TEAM_RED);
        redScore--;
        data.setScore(Constants.TEAM_RED, redScore);
        lblRedScore.setText("" + redScore);
        logString("Red score (Team #" + data.getTeamNumber(Constants.TEAM_RED) + ") decreased to " + redScore);
    }//GEN-LAST:event_cmdRedScoreDown_clicked
    
    private void cmdRedScoreUp_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdRedScoreUp_clicked
        int redScore = data.getScore(Constants.TEAM_RED);
        redScore++;
        data.setScore(Constants.TEAM_RED, redScore);
        lblRedScore.setText("" + redScore);
        cmdBlueKickOff.setSelected(true);
        data.setKickOffTeam(Constants.TEAM_BLUE);
        
        GUIUnpenaliseAll();
        data.setGameState(Constants.STATE_READY);
        cmdReady.setSelected(true);
        logString("Red score (Team #" + data.getTeamNumber(Constants.TEAM_RED) + ") increased to " + redScore);
    }//GEN-LAST:event_cmdRedScoreUp_clicked
    
    private void cmdBlueScoreDown_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdBlueScoreDown_clicked
        int blueScore = data.getScore(Constants.TEAM_BLUE);
        blueScore--;
        data.setScore(Constants.TEAM_BLUE, blueScore);
        lblBlueScore.setText("" + blueScore);
        logString("Blue score (Team #" + data.getTeamNumber(Constants.TEAM_BLUE) + ") decreased to " + blueScore);
    }//GEN-LAST:event_cmdBlueScoreDown_clicked
    
    private void cmdBlueScoreUp_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdBlueScoreUp_clicked
        int blueScore = data.getScore(Constants.TEAM_BLUE);
        blueScore++;
        data.setScore(Constants.TEAM_BLUE, blueScore);
        lblBlueScore.setText("" + blueScore);
        cmdRedKickOff.setSelected(true);
        data.setKickOffTeam(Constants.TEAM_RED);
        
        GUIUnpenaliseAll();
        data.setGameState(Constants.STATE_READY);
        cmdReady.setSelected(true);
        logString("Blue score (Team #" + data.getTeamNumber(Constants.TEAM_BLUE) + ") increased to " + blueScore);
    }//GEN-LAST:event_cmdBlueScoreUp_clicked
                  
    
    
    /***************************************************************************
     *
     *  Applies the selected penalty / player combination to the data structure
     *
     **************************************************************************/
    // when both of these are true the condition to send out a penalty is met
    private boolean penaltySelected = false;
    private boolean playerSelected  = false;
        
    // translate the penalty name into the penalty constant
    private int getPenaltyConst(String name) {
        
        if (name.equals("Ball Holding")) {
            return Constants.PENALTY_BALL_HOLDING;
        } else if (name.equals("Goalie Pushing")) {
            return Constants.PENALTY_GOALIE_PUSHING;
        } else if (name.equals("Player Pushing")) {
            return Constants.PENALTY_PLAYER_PUSHING;
        } else if (name.equals("Damage")) {
            return Constants.PENALTY_DAMAGE;
        } else if (name.equals("Obstruction")) {
            return Constants.PENALTY_OBSTRUCTION;
        } else if (name.equals("Leaving Field")) {
            return Constants.PENALTY_LEAVING;
        } else if (name.equals("Illegal Defender")) {
            return Constants.PENALTY_ILLEGAL_DEFENDER;
        } else if (name.equals("Illegal Defense")) {
            return Constants.PENALTY_ILLEGAL_DEFENSE;
        } else if (name.equals("Pick Up Request")) {
            return Constants.PENALTY_REQ_FOR_PICKUP;
        }
        return Constants.PENALTY_NONE;
    }
    
    
    // this function is called by the player/penalty button clicks
    // it applies the selected penalty to the selected player(s)
    private void applyPenalty() {
        
        // only execute if player(s) and a penalty is selected
        if (!playerSelected || !penaltySelected) { return; }
        
        String text = null;     // name of the penalty
        int penaltyConst;       // constant number of the penalty
        
        int team     = 0;       // team of the player to penalise
        int playerNo = 0;       // player number to penalise
        
        
        // find the name of the selected penalty
        for (Enumeration penalty = penaltyButtons.getElements() ; penalty.hasMoreElements() ;) {
            JToggleButton button = (JToggleButton)(penalty.nextElement());
            if (button.isSelected()) {
                text = button.getText();
                break;
            }
        }
                
        if (text.equals("Invisible")) { return; }
        penaltyConst = getPenaltyConst(text);
        if (Constants.debug) { System.out.println("Applying " + text + " to..."); }
                
        // apply the selected penalty to the selected player(s)
        if (cmdBlue1.isSelected()) {
            if (Constants.debug) { System.out.println("\tBlue 1"); }
            data.setPenalty(Constants.TEAM_BLUE, 0, penaltyConst);
            blueCountdown[0] = GUIPenalty(penaltyConst, Constants.TEAM_BLUE, 1);
	        logString("Penalise Blue 1: " + text);
        }
        if (cmdBlue2.isSelected()) {
            if (Constants.debug) { System.out.println("\tBlue 2"); }
            data.setPenalty(Constants.TEAM_BLUE, 1, penaltyConst);
            blueCountdown[1] = GUIPenalty(penaltyConst, Constants.TEAM_BLUE, 2);
	        logString("Penalise Blue 2: " + text);
        }
        if (cmdBlue3.isSelected()) {
            if (Constants.debug) { System.out.println("\tBlue 3"); }
            data.setPenalty(Constants.TEAM_BLUE, 2, penaltyConst);
            blueCountdown[2] = GUIPenalty(penaltyConst, Constants.TEAM_BLUE, 3);
	        logString("Penalise Blue 3: " + text);
        }
        if (cmdBlue4.isSelected()) {
            if (Constants.debug) { System.out.println("\tBlue 4"); }
            data.setPenalty(Constants.TEAM_BLUE, 3, penaltyConst);
            blueCountdown[3] = GUIPenalty(penaltyConst, Constants.TEAM_BLUE, 4);
	        logString("Penalise Blue 4: " + text);
        }
        if (cmdRed1.isSelected()) {
            if (Constants.debug) { System.out.println("\tRed 1"); }
            data.setPenalty(Constants.TEAM_RED, 0, penaltyConst);
            redCountdown[0] = GUIPenalty(penaltyConst, Constants.TEAM_RED, 1);
	        logString("Penalise Red 1: " + text);
        }
        if (cmdRed2.isSelected()) {
            if (Constants.debug) { System.out.println("\tRed 2"); }
            data.setPenalty(Constants.TEAM_RED, 1, penaltyConst);
            redCountdown[1] = GUIPenalty(penaltyConst, Constants.TEAM_RED, 2);
	        logString("Penalise Red 2: " + text);
        }
        if (cmdRed3.isSelected()) {
            if (Constants.debug) { System.out.println("\tRed 3"); }
            data.setPenalty(Constants.TEAM_RED, 2, penaltyConst);
            redCountdown[2] = GUIPenalty(penaltyConst, Constants.TEAM_RED, 3);
	        logString("Penalise Red 3: " + text);
        }
        if (cmdRed4.isSelected()) {
            if (Constants.debug) { System.out.println("\tRed 4"); }
            data.setPenalty(Constants.TEAM_RED, 3, penaltyConst);
            redCountdown[3] = GUIPenalty(penaltyConst, Constants.TEAM_RED, 4);
	        logString("Penalise Red 4: " + text);
        }
        
        unselectPenalty();      // clear all selections after a penalty is given        
    }
    
    
    // returns the appropriate penalty time based on the penalty given
    private int getPenaltyTime(int penaltyConst, int player) {
        if ((player == Constants.GOALIE_PLAYER_NUMBER &&
                penaltyConst != Constants.PENALTY_REQ_FOR_PICKUP) ||
            penaltyConst == Constants.PENALTY_NONE) {
            return Constants.PENALTY_SHORT_TIME;
        }
        return Constants.PENALTY_LONG_TIME;
    }
    
    
    // changes the GUI penalty buttons to reflect a sanctioned penalty
    // it also returns the duration of the penalty
    private int GUIPenalty(int penaltyConst, int team, int player) {
                
        if (penaltyConst == Constants.PENALTY_NONE) { return 0; }
        
        int    time  = getPenaltyTime(penaltyConst, player);
        String label = "Unpenalise (" + time + ")";
               
        if (team == Constants.TEAM_BLUE && player == 1) {
            cmdBlue1Unpenalise.setText(label);
            cmdBlue1Unpenalise.setEnabled(true);
        } else if (team == Constants.TEAM_BLUE && player == 2) {
            cmdBlue2Unpenalise.setText(label);
            cmdBlue2Unpenalise.setEnabled(true);            
        } else if (team == Constants.TEAM_BLUE && player == 3) {
            cmdBlue3Unpenalise.setText(label);
            cmdBlue3Unpenalise.setEnabled(true);            
        } else if (team == Constants.TEAM_BLUE && player == 4) {
            cmdBlue4Unpenalise.setText(label);
            cmdBlue4Unpenalise.setEnabled(true);                            
        } else if (team == Constants.TEAM_RED && player == 1) {
            cmdRed1Unpenalise.setText(label);
            cmdRed1Unpenalise.setEnabled(true);
        } else if (team == Constants.TEAM_RED && player == 2) {
            cmdRed2Unpenalise.setText(label);
            cmdRed2Unpenalise.setEnabled(true);
        } else if (team == Constants.TEAM_RED && player == 3) {                
            cmdRed3Unpenalise.setText(label);
            cmdRed3Unpenalise.setEnabled(true);            
        } else if (team == Constants.TEAM_RED && player == 4) {            
            cmdRed4Unpenalise.setText(label);
            cmdRed4Unpenalise.setEnabled(true);    
        }
        return time;
    }
    
    
    // unselects everything in the GUI for a penalty
    private void unselectPenalty() {
        playerSelected  = false;
        penaltySelected = false;
        cmdInvisible.setSelected(true);     // don't select any penalty
        cmdBlue1.setSelected(false);
        cmdBlue2.setSelected(false);
        cmdBlue3.setSelected(false);
        cmdBlue4.setSelected(false);
        cmdRed1.setSelected(false);
        cmdRed2.setSelected(false);
        cmdRed3.setSelected(false);
        cmdRed4.setSelected(false);        
    }
    
    
    // Player/Penalty button clicks
    private void cmdBlue_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdBlue_clicked
        if (Constants.debug) { System.out.println("Clicked on Blue player"); }
        playerSelected = true; applyPenalty();
    }//GEN-LAST:event_cmdBlue_clicked
    
    private void cmdRed_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdRed_clicked
        if (Constants.debug) { System.out.println("Clicked on Red player"); }
        playerSelected = true; applyPenalty();
    }//GEN-LAST:event_cmdRed_clicked
    
    private void penalty_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_penalty_clicked
        if (Constants.debug) { System.out.println("Clicked on a penalty"); }
        penaltySelected = true; applyPenalty();
    }//GEN-LAST:event_penalty_clicked
    
    
    // Game Button clicks, these update the data structure and put the game into
    // a specified state, some of these states also reset all penalties
    private void cmdRedDropIn_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdRedDropIn_clicked
        if (Constants.debug) { System.out.println("Clicked on Red Drop In"); }
        data.setDropInTeam(Constants.TEAM_RED);
	    logString("Ball knocked out by Red");
    }//GEN-LAST:event_cmdRedDropIn_clicked
    
    // start the timer on kick off
    private void cmdKickOff_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdKickOff_clicked
        if (Constants.debug) { System.out.println("Clicked on Play - burst"); }
        data.setGameState(Constants.STATE_PLAYING);
        GUIUnpenaliseAll();
        startClock = true;
        broadcast.setBurst(true);
        if (halfKickoffTeam == -1) {
        	halfKickoffTeam = data.getKickOffTeam();
	    	logString("Remembering initial kickoff colour: " +
	    			(data.getKickOffTeam()==Constants.TEAM_RED?"Red":"Blue"));
        }
	    logString("Kickoff! " + (data.getKickOffTeam()==Constants.TEAM_RED?"Red":"Blue") +
	    		" kicking off with " + getGameTimeString() + " remaining in half.");
    }//GEN-LAST:event_cmdKickOff_clicked
    
    private void cmdFinish_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdFinish_clicked
        if (Constants.debug) { System.out.println("Clicked on Finished"); }
        data.setGameState(Constants.STATE_FINISHED);
        GUIUnpenaliseAll();
        startClock = false; 
	    logString("Finsh.  Score: " +
	    	"Team #" + data.getTeamNumber(Constants.TEAM_BLUE) + " : " + data.getScore(Constants.TEAM_BLUE) + " goals" +
	    	", Team #" + data.getTeamNumber(Constants.TEAM_RED) + " : " + data.getScore(Constants.TEAM_RED) + " goals");
    }//GEN-LAST:event_cmdFinish_clicked
    
    private void cmdSet_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdSet_clicked
        if (Constants.debug) { System.out.println("Clicked on Set"); }
        data.setGameState(Constants.STATE_SET);
        GUIUnpenaliseAll();
	    logString("Set");
    }//GEN-LAST:event_cmdSet_clicked
    
    private void cmdReady_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdReady_clicked
        if (Constants.debug) { System.out.println("Clicked on Ready"); }
        data.setGameState(Constants.STATE_READY);
        GUIUnpenaliseAll();
	    logString("Ready");
    }//GEN-LAST:event_cmdReady_clicked
    
    private void cmdInitial_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdInitial_clicked
        if (Constants.debug) { System.out.println("Clicked on Initial"); }
        data.setGameState(Constants.STATE_INITIAL);
        GUIUnpenaliseAll();
	    logString("Initial");
    }//GEN-LAST:event_cmdInitial_clicked
    
    private void cmdRedKickOff_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdRedKickOff_clicked
        if (Constants.debug) { System.out.println("Clicked on Red Kick Off"); }
        data.setKickOffTeam(Constants.TEAM_RED);
	    logString("Switching to Red to kick off");
    }//GEN-LAST:event_cmdRedKickOff_clicked
    
    private void cmdBlueKickOff_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdBlueKickOff_clicked
        if (Constants.debug) { System.out.println("Clicked on Blue Kick Off"); }
        data.setKickOffTeam(Constants.TEAM_BLUE);
	    logString("Switching to Blue to kick off");
    }//GEN-LAST:event_cmdBlueKickOff_clicked
    
    private void cmdBlueDropIn_clicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cmdBlueDropIn_clicked
        if (Constants.debug) { System.out.println("Clicked on Blue Drop In"); }
        data.setDropInTeam(Constants.TEAM_BLUE);
	    logString("Ball knocked out by Blue");
    }//GEN-LAST:event_cmdBlueDropIn_clicked
    
    private void close(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_close
        if (Constants.debug) { System.out.println("GUI closing"); }
        broadcast.socketCleanup();
	    logString("Closing");
        log.close();
        log = null;
    }//GEN-LAST:event_close
    
    
    
    /*******************************************************************************
     *
     *  A timer to time the penalty countdown and the clock
     *
     *******************************************************************************/
    private static int oneSecond = 1000;        // interval of clock event
    
    // initial clock values
    private static int secs = 0;
    private static int mins = Constants.TIME_MINUTES;
    
    // flags set by clock buttons to start/stop it
    private static boolean startClock = false;
    private static boolean countForward = false;
    
    // call other methods that need the time
    private static ActionListener clock = new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
            updateClock();                  // update GUI clock
            updatePenaltyCountDown();       // update penalty button countdowns
            updateEstimatedSecs();          // update secs remaining in struct
            data.updateDropInTime();        // update drop in team/time
        }
    };
     
    
    // updates the number of seconds remaining in the half
    // also indicates whether overtime has occurred using true/false
    private static void updateEstimatedSecs() {
        if (lblTime.getForeground().equals(Color.RED)) {
            data.setEstimatedSecs(0, true);
        } else {
            data.setEstimatedSecs((60*mins)+secs, false);
        }
    }
            
    
    // these arrays hold the penalty countdown values for each player
    private static int[] blueCountdown = new int[4];   // 4 robots per team
    private static int[] redCountdown  = new int[4];
    
    // update any penalty countdowns in both the data structure and the GUI
    private static void updatePenaltyCountDown() {
        
        // decrement the counters where necessary
        for (int i=0; i<4; i++) { 
            if (blueCountdown[i] > 0) { blueCountdown[i]--; }            
        }

        for (int i=0; i<4; i++) {
            if (redCountdown[i] > 0) { redCountdown[i]--; }            
        }
        
        // show the new times on the buttons if the player is currently penalised
        if (data.getPenalty(Constants.TEAM_BLUE, 0) != Constants.PENALTY_NONE) {
            cmdBlue1Unpenalise.setText("Unpenalise (" + blueCountdown[0] + ")"); 
            data.setSecsTillUnpenalised(Constants.TEAM_BLUE, 0, blueCountdown[0]);
        }
        
        if (data.getPenalty(Constants.TEAM_BLUE, 1) != Constants.PENALTY_NONE) {
            cmdBlue2Unpenalise.setText("Unpenalise (" + blueCountdown[1] + ")"); 
            data.setSecsTillUnpenalised(Constants.TEAM_BLUE, 1, blueCountdown[1]);
        }
        
        if (data.getPenalty(Constants.TEAM_BLUE, 2) != Constants.PENALTY_NONE) {
            cmdBlue3Unpenalise.setText("Unpenalise (" + blueCountdown[2] + ")"); 
            data.setSecsTillUnpenalised(Constants.TEAM_BLUE, 2, blueCountdown[2]);
        }
                
        if (data.getPenalty(Constants.TEAM_BLUE, 3) != Constants.PENALTY_NONE) {
            cmdBlue4Unpenalise.setText("Unpenalise (" + blueCountdown[3] + ")"); 
            data.setSecsTillUnpenalised(Constants.TEAM_BLUE, 3, blueCountdown[3]);
        }
        
        if (data.getPenalty(Constants.TEAM_RED, 0) != Constants.PENALTY_NONE) {
            cmdRed1Unpenalise.setText("Unpenalise (" + redCountdown[0] + ")"); 
            data.setSecsTillUnpenalised(Constants.TEAM_RED, 0, redCountdown[0]);
        }
        
        if (data.getPenalty(Constants.TEAM_RED, 1) != Constants.PENALTY_NONE) {
            cmdRed2Unpenalise.setText("Unpenalise (" + redCountdown[1] + ")"); 
            data.setSecsTillUnpenalised(Constants.TEAM_RED, 1, redCountdown[1]);
        }
        
        if (data.getPenalty(Constants.TEAM_RED, 2) != Constants.PENALTY_NONE) {
            cmdRed3Unpenalise.setText("Unpenalise (" + redCountdown[2] + ")"); 
            data.setSecsTillUnpenalised(Constants.TEAM_RED, 2, redCountdown[2]);
        }
                    
        if (data.getPenalty(Constants.TEAM_RED, 3) != Constants.PENALTY_NONE) {
            cmdRed4Unpenalise.setText("Unpenalise (" + redCountdown[3] + ")"); 
            data.setSecsTillUnpenalised(Constants.TEAM_RED, 3, redCountdown[3]);
        }
        
        penaltyColours();
    }
    
    
    // changes the penalty button colour to green when the time is 5 or less seconds
    private static void penaltyColours() {
        boolean beep = false;
        
        if (blueCountdown[0] <= 5 && data.getPenalty(Constants.TEAM_BLUE, 0) != Constants.PENALTY_NONE) {
        	beep = (beep || blueCountdown[0] == 5 || blueCountdown[0] == 0);
            cmdBlue1Unpenalise.setBackground(Color.GREEN); }
        if (blueCountdown[1] <= 5 && data.getPenalty(Constants.TEAM_BLUE, 1) != Constants.PENALTY_NONE) {
        	beep = (beep || blueCountdown[1] == 5 || blueCountdown[1] == 0);
            cmdBlue2Unpenalise.setBackground(Color.GREEN); }
        if (blueCountdown[2] <= 5 && data.getPenalty(Constants.TEAM_BLUE, 2) != Constants.PENALTY_NONE) {
        	beep = (beep || blueCountdown[2] == 5 || blueCountdown[2] == 0);
            cmdBlue3Unpenalise.setBackground(Color.GREEN); }
        if (blueCountdown[3] <= 5 && data.getPenalty(Constants.TEAM_BLUE, 3) != Constants.PENALTY_NONE) {
        	beep = (beep || blueCountdown[3] == 5 || blueCountdown[3] == 0);
            cmdBlue4Unpenalise.setBackground(Color.GREEN); }
        if (redCountdown[0] <= 5 && data.getPenalty(Constants.TEAM_RED, 0) != Constants.PENALTY_NONE) {
        	beep = (beep || redCountdown[0] == 5 || redCountdown[0] == 0);
            cmdRed1Unpenalise.setBackground(Color.GREEN); }
        if (redCountdown[1] <= 5 && data.getPenalty(Constants.TEAM_RED, 1) != Constants.PENALTY_NONE) {
        	beep = (beep || redCountdown[1] == 5 || redCountdown[1] == 0);
            cmdRed2Unpenalise.setBackground(Color.GREEN); }
        if (redCountdown[2] <= 5 && data.getPenalty(Constants.TEAM_RED, 2) != Constants.PENALTY_NONE) {
        	beep = (beep || redCountdown[2] == 5 || redCountdown[2] == 0);
            cmdRed3Unpenalise.setBackground(Color.GREEN); }
        if (redCountdown[3] <= 5 && data.getPenalty(Constants.TEAM_RED, 3) != Constants.PENALTY_NONE) {
        	beep = (beep || redCountdown[3] == 5 || redCountdown[3] == 0);
            cmdRed4Unpenalise.setBackground(Color.GREEN); }

		if (beep) {
			Toolkit.getDefaultToolkit().beep();
		}
    }
    
    
    // count down the clock
    private static void updateClock() {
        
        // the clock stops if auto pause is enabled and the game is in the 
        // "Ready" or "Set" states
        if ((data.getGameState() == Constants.STATE_READY ||
             data.getGameState() == Constants.STATE_SET) &&
             chkAutoPause.isSelected()) {
            if (startClock) {
            	logString("Auto pausing clock at time " + getGameTimeString());
            }
            startClock = false;
        }
        
        cmbHalf.setEnabled(!startClock);
        
        // only run if enabled, the clock becomes editable if not running
        if (!startClock) { 
            lblTime.setEnabled(true);
            lblTime.setForeground(Color.BLACK);     // use black when editing
            return; 
        } else {                    
            lblTime.setEnabled(false);
        }
        
        boolean beep = false;
        
        // do countdown / countup
        if (countForward == false) {
            lblTime.setDisabledTextColor(Color.GRAY);       // use gray when 
            lblTime.setForeground(Color.GRAY);              // counting down
            secs--;
            if (secs < 0) {
                secs = 59;
                mins--;
            }
            if (mins == 0 && secs <= 10) {
            	beep = true;
            }
            if (secs == 0 && mins == 0) {
            	countForward = true;
            }
        } else {
            lblTime.setDisabledTextColor(Color.RED);        // count up with red
            lblTime.setForeground(Color.RED);     
            secs++;
            beep = true;
            if (secs > 59) {
                secs = 0;
                mins++;
            }
        }
        
        if (startClock && beep) {
        	Toolkit.getDefaultToolkit().beep();
        }
        
        StringBuffer minsLeft = new StringBuffer();
        StringBuffer secsLeft = new StringBuffer();
        DecimalFormat format = new DecimalFormat("00");
        DecimalFormat secFormat = new DecimalFormat("00");
        
        format.format(mins, minsLeft, new FieldPosition(0));
        format.format(secs, secsLeft, new FieldPosition(0));
        lblTime.setText(minsLeft + ":" + secsLeft);
        
    }

    
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private static javax.swing.JCheckBox chkAutoPause;
    private static javax.swing.JComboBox cmbHalf;
    private javax.swing.JToggleButton cmdBallHolding;
    private javax.swing.JToggleButton cmdBlue1;
    private static javax.swing.JButton cmdBlue1Unpenalise;
    private javax.swing.JToggleButton cmdBlue2;
    private static javax.swing.JButton cmdBlue2Unpenalise;
    private javax.swing.JToggleButton cmdBlue3;
    private static javax.swing.JButton cmdBlue3Unpenalise;
    private javax.swing.JToggleButton cmdBlue4;
    private static javax.swing.JButton cmdBlue4Unpenalise;
    private javax.swing.JButton cmdBlueDropIn;
    private javax.swing.JToggleButton cmdBlueKickOff;
    private javax.swing.JButton cmdBlueScoreDown;
    private javax.swing.JButton cmdBlueScoreUp;
    private javax.swing.JToggleButton cmdDamage;
    private javax.swing.JToggleButton cmdFinish;
    private javax.swing.JToggleButton cmdGoaliePushing;
    private javax.swing.JToggleButton cmdIllegalDefender;
    private javax.swing.JToggleButton cmdIllegalDefense;
    private javax.swing.JToggleButton cmdInitial;
    private javax.swing.JToggleButton cmdInvisible;
    private javax.swing.JToggleButton cmdKickOff;
    private javax.swing.JToggleButton cmdLeavingField;
    private javax.swing.JToggleButton cmdObstruction;
    private javax.swing.JToggleButton cmdPickupRequest;
    private javax.swing.JToggleButton cmdPlayerPushing;
    private javax.swing.JToggleButton cmdReady;
    private javax.swing.JToggleButton cmdRed1;
    private static javax.swing.JButton cmdRed1Unpenalise;
    private javax.swing.JToggleButton cmdRed2;
    private static javax.swing.JButton cmdRed2Unpenalise;
    private javax.swing.JToggleButton cmdRed3;
    private static javax.swing.JButton cmdRed3Unpenalise;
    private javax.swing.JToggleButton cmdRed4;
    private static javax.swing.JButton cmdRed4Unpenalise;
    private javax.swing.JButton cmdRedDropIn;
    private javax.swing.JToggleButton cmdRedKickOff;
    private javax.swing.JButton cmdRedScoreDown;
    private javax.swing.JButton cmdRedScoreUp;
    private javax.swing.JToggleButton cmdSet;
    private javax.swing.JButton cmdTimeReset;
    private javax.swing.JButton cmdTimeStart;
    private javax.swing.ButtonGroup gameButtons;
    private javax.swing.ButtonGroup kickOffButtons;
    private javax.swing.JLabel lblBlueScore;
    private javax.swing.JLabel lblBlueTeam;
    private javax.swing.JLabel lblRedScore;
    private javax.swing.JLabel lblRedTeam;
    private static javax.swing.JTextField lblTime;
    private javax.swing.ButtonGroup penaltyButtons;
    private javax.swing.JPanel pnlBlue;
    private javax.swing.JPanel pnlKickOff;
    private javax.swing.JPanel pnlMain;
    private javax.swing.JPanel pnlPenalties;
    private javax.swing.JPanel pnlRed;
    private javax.swing.JPanel pnlScore;
    private javax.swing.JPanel pnlStatus;
    private javax.swing.JPanel pnlTime;
    // End of variables declaration//GEN-END:variables
    
}
