package interfacing;

import generating.GeneratorsController;

import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Dimension;

import java.io.BufferedReader;
import java.io.FileReader;

import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;


import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ConnectException;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

import javax.swing.ImageIcon;
import javax.swing.JButton;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JTabbedPane;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.filechooser.FileFilter;

import logging.Reporter;
import logging.SavingRunnable;
import logging.ServersReader;
import mapping.AvailabilityServer;
import mapping.CompletionServer; //import mapping.FCFS_MS;
import mapping.LPAS_DG_MS;
import mapping.Mapper;

import mapping.data.JobClassesTable;
import mapping.data.ServersTable;

import java.net.InetSocketAddress;

/**
 * This class is the Main Frame that contains all other GUI containers and
 * components. It has a Mapper object with all the tables. Essentially it is the
 * controller and the view of all the data in the system.
 * 
 * @author Majd Kokaly
 * 
 */
public class MainFrame extends JFrame implements ActionListener, WindowListener {
	/**
	 * The serialVersionUID is used to universally identify this version of this
	 * class.
	 */
	private static final long serialVersionUID = 3585617362601575717L;

	/** The main Mapper object that contains all the tables and other data */
	private Mapper mapper;

	/** Server that receives the availability info */
	private AvailabilityServer availabilityServer;

	/** Server that receives completion notifications */
	private CompletionServer completionServer;

	/** Thread that saves the files of the results during a test * */
	private SavingRunnable savingRunnable;

	/** The main Tabbed Pane */
	protected JTabbedPane tabbedPane;

	/** Definitions Scroll Pane */
	private JScrollPane definitionScrollPane;

	/** Data Tabbed Pane */
	protected JTabbedPane serversAndJobsClassesTablesTabbedPane;

	/** This panel is used to control some system level parameters */
	private SystemLevelParametersJPanel systemLevelParametersJPanel;

	/** The ServersTableJPanel that contains the servers Table */
	private ServersTableJPanel serversTableJPanel;

	/** The JobClassesTableJPanel that contains the job Classes Table */
	private JobClassesTableJPanel jobClassesTableJPanel;

	/**
	 * The serversMessagesJPanel that contains the servers Table and controls
	 * the availability of each
	 */
	private ServersAvailabilityJPanel serversAvailabilityPanel;

	/** Monitoring Scroll Pane */
	private JScrollPane monitoringScrollPane;
	/** Monitoring Tabbed Pane */
	private JTabbedPane monitoringTabbedPane;
	/** Panel containing the jobs table */
	private JobsTableJPanel jobsTableJPanel;
	/**
	 * Panel containing a JTable that contains a representation of the current
	 * queue of available servers
	 */
	private AvailableServerQueueJPanel availableServersJPanel;

	/** Statistics Scroll Pane for Generating */
	private JScrollPane statisticsScrollPane;
	/** Statistics Tabbed Pane */
	protected JTabbedPane statisticsTabbedPane;

	/** This panel shows statistics about the job classes */
	private JobClassesStatisticsJPanel jobClassesJPanel;

	/** The controller that controls the group of threads that generate jobs */
	private GeneratorsController generatorsController;
		
	/** Button that runs the system */
	private JButton startJButton;

	/** Button that stops the system */
	private JButton stopJButton;

	/** Button that saves tables */
	private JButton saveJButton;

	/** Button that loads tables */
	private JButton loadJButton;

	/** Button that prints the mapper on standard input */
	private JButton printMapperJButton;

	/** Button that regenerates all failures traces */
	private JButton failureTraceButton;

	/** Button that recalculates all processing rates */
	private JButton fillProcessingRate;

	/** Button that sends a start signal to the servers */
	private JButton startServers;

	/** Button that sends a pause signal to the servers */
	private JButton pauseServers;

	/** Button that sends a kill signal to the servers */
	private JButton killServers;

	/** The Filename of the Job Classes File*/
	private String Readfile;
	
	/** JPanel that contains the controlling button */
	JToolBar toolBar = new JToolBar("Still draggable");

	/** Menu bar of the software */
	private JMenuBar menuBar;

	private final int ITEM_PLAIN = 0; // Item types
	private final int ITEM_CHECK = 1;
	private final int ITEM_RADIO = 2;

	private JMenu menuAction;
	private JMenuItem menuItemStart = null;
	private JMenuItem menuItemStop = null;
	private JMenuItem menuItemFailureTraces = null;
	private JMenuItem menuItemProcessingRates = null;

	private JMenu menuTables;
	private JMenuItem menuItemSaveDefinitions = null;
	private JMenuItem menuItemLoadDefinitions = null;

	private JMenuItem menuItemSaveSystemStats = null;
	private JMenuItem menuItemSaveJobClassesStats = null;
	private JMenuItem menuItemSaveMueMatrix = null;
	private JMenuItem menuItemSaveJobTable = null;
	private JMenuItem menuItemSaveAll = null;

	private JMenu menuServers;
	private JMenuItem menuItemStartServers = null;
	private JMenuItem menuItemPingServers = null;
	private JMenuItem menuItemPauseServers = null;
	private JMenuItem menuItemKillServers = null;

	//private JMenu menuExport;
	private JMenuItem menuItemPrint = null;

	private JMenu menuHelp;
	private JMenuItem menuItemManual = null;
	private JMenuItem menuItemThesis = null;

	private JMenu menuAbout;
	private JMenuItem menuItemAbout = null;

	public static final String iconsFolder = "Icons";

	public String getReadFile()
	{
		return this.Readfile; 
	}
	
	public void setReadFile(String readfile)
	{
		this.Readfile = readfile;
	}
	/**
	 * The default constructor. It initialize and lays the GUI objects.
	 */
	
	/* ------------------------- */

	public MainFrame() {
		/* Initializing Data members */
		this.setMapper(new Mapper());

		// Default value is two minutes
		this.getMapper().setTimeUnitInMinutes(2.0);
		
		try
		{
			this.buildJobClasses();
		}
		catch(IOException e)
		{
			e.printStackTrace();
		}
		
		this.buildServers();

		// Default choice. LPAS_DG_MS
		this.getMapper().setMappingScheme(new LPAS_DG_MS(this.getMapper()));

		this.availabilityServer = new AvailabilityServer(this.getMapper());

		this.completionServer = new CompletionServer(this.getMapper());

		this.getMapper().fillAllFailureTraces();

		this.setGeneratorsController(new GeneratorsController(this.getMapper()));

		this.savingRunnable = new SavingRunnable(this.getMapper(), this.getGeneratorsController(), 20);

		/* ------------------------------------------------ */
		/* GUI */
		/* ------------------------------------------------ */
		/* Icons */

		this.tabbedPane = new JTabbedPane();

		/* ------- Definition ------- */
		this.serversAndJobsClassesTablesTabbedPane = new JTabbedPane();
		this.systemLevelParametersJPanel = new SystemLevelParametersJPanel(this);
		this.jobClassesTableJPanel = new JobClassesTableJPanel(this);
		this.serversTableJPanel = new ServersTableJPanel(this);
		this.serversAvailabilityPanel = new ServersAvailabilityJPanel(this);
		this.getMapper().getServersTable().addServersListener(this.serversTableJPanel);
		this.serversAndJobsClassesTablesTabbedPane.setTabPlacement(JTabbedPane.LEFT);
		this.serversAndJobsClassesTablesTabbedPane.add("General", this.systemLevelParametersJPanel);
		this.serversAndJobsClassesTablesTabbedPane.add("Job Classes", this.jobClassesTableJPanel);
		this.serversAndJobsClassesTablesTabbedPane.add("Servers", this.serversTableJPanel);
		this.serversAndJobsClassesTablesTabbedPane.add("Availability", this.serversAvailabilityPanel);
		this.serversAndJobsClassesTablesTabbedPane.add("LP", new LP_JPanel(this));
		definitionScrollPane = new JScrollPane(serversAndJobsClassesTablesTabbedPane);

		/* ------- Monitoring------- */
		this.monitoringTabbedPane = new JTabbedPane();
		this.jobsTableJPanel = new JobsTableJPanel(this);
		this.availableServersJPanel = new AvailableServerQueueJPanel(this);
		this.getMapper().getJobsTable().addJobsTableListener(jobsTableJPanel);

		this.monitoringTabbedPane.setTabPlacement(JTabbedPane.LEFT);
		this.monitoringTabbedPane.add("Jobs Table", this.jobsTableJPanel);
		this.monitoringTabbedPane.add("Available Servers", this.availableServersJPanel);
		this.monitoringScrollPane = new JScrollPane(monitoringTabbedPane);

		/* ------- Statistics ------- */
		this.statisticsTabbedPane = new JTabbedPane();
		this.statisticsTabbedPane.setTabPlacement(JTabbedPane.LEFT);
		this.statisticsTabbedPane.add("General", new SystemLevelStatisticsJPanel(this));
		this.jobClassesJPanel = new JobClassesStatisticsJPanel(this);
		this.jobClassesJPanel.setEnabled(false);
		this.statisticsTabbedPane.add("Job Classes", this.jobClassesJPanel);
		this.statisticsTabbedPane.add("Processing Rates", new MueJPanel(this));
		this.statisticsScrollPane = new JScrollPane(statisticsTabbedPane);

		/* ------- Add JPanels to tabbedPane ------- */
		tabbedPane.add("Definitions", definitionScrollPane);
		tabbedPane.add("Monitoring", monitoringScrollPane);
		tabbedPane.add("Statistics", statisticsScrollPane);

		/*
		 * Initialize the JPanel of the controlling buttons
		 */
		this.startJButton = new JButton("Start");
		this.stopJButton = new JButton("Stop");
		this.saveJButton = new JButton("Save Definitions");
		this.loadJButton = new JButton("Load Definitions");
		this.printMapperJButton = new JButton("Print");
		this.failureTraceButton = new JButton("Fill Traces");
		this.fillProcessingRate = new JButton("Calculate Processing Rates");
		this.startServers = new JButton("Start Servers");
		this.pauseServers = new JButton("Pause Servers");
		this.killServers = new JButton("Kill Servers");

		/* ICONS */
		this.startJButton
				.setIcon(new ImageIcon(iconsFolder+ "/" + "player_play_22.png"));
		this.stopJButton.setIcon(new ImageIcon(iconsFolder + "/" + "player_stop_22.png"));

		this.saveJButton.setIcon(new ImageIcon(iconsFolder + "/" + "filesave_22.png"));
		this.loadJButton.setIcon(new ImageIcon(iconsFolder + "/" + "filerevert_22.png"));

		this.failureTraceButton
				.setIcon(new ImageIcon(iconsFolder + "/" + "reload_22.png"));
		this.fillProcessingRate
				.setIcon(new ImageIcon(iconsFolder + "/" + "reload_22.png"));

		this.startServers
				.setIcon(new ImageIcon(iconsFolder + "/" + "player_play_22.png"));
		this.pauseServers
				.setIcon(new ImageIcon(iconsFolder + "/" + "player_pause_22.png"));
		this.killServers.setIcon(new ImageIcon(iconsFolder + "/" + "reload_22.png"));

		this.printMapperJButton.setIcon(new ImageIcon(iconsFolder + "/" + "fileprint_22.png"));

		/* Tool Tips */
		this.startJButton.setToolTipText("Starts the operations of generations and mapping.");
		this.stopJButton.setToolTipText("Stops the operations of generations and mapping.");

		this.saveJButton.setToolTipText("Saves the definitions.");
		this.loadJButton.setToolTipText("Loades the definitions");

		this.failureTraceButton.setToolTipText("Generate a failure trace for all servers.");
		this.fillProcessingRate.setToolTipText("Refresh the processing Rates.");

		this.startServers.setToolTipText("Starts the Pullers on servers");
		this.pauseServers.setToolTipText("Stop the Pullers on servers");
		this.killServers.setToolTipText("Kill the Pullers on servers");

		this.printMapperJButton.setToolTipText("Starts the operations of generations and mapping.");

		/* Creating the tool bar */
		this.toolBar.setPreferredSize(new Dimension(70, 25));
		this.toolBar.add(saveJButton);
		this.toolBar.add(loadJButton);
		this.toolBar.addSeparator(new Dimension(10, 10));
		this.toolBar.add(failureTraceButton);
		this.toolBar.add(fillProcessingRate);
		this.toolBar.addSeparator(new Dimension(10, 10));
		this.toolBar.add(startServers);
		this.toolBar.add(pauseServers);
		this.toolBar.addSeparator(new Dimension(10, 10));
		this.toolBar.add(startJButton);
		this.toolBar.add(stopJButton);
		this.toolBar.addSeparator(new Dimension(10, 10));

		this.startJButton.addActionListener(this);
		this.stopJButton.addActionListener(this);
		this.saveJButton.addActionListener(this);
		this.loadJButton.addActionListener(this);
		this.printMapperJButton.addActionListener(this);
		this.failureTraceButton.addActionListener(this);
		this.fillProcessingRate.addActionListener(this);
		this.startServers.addActionListener(this);
		this.pauseServers.addActionListener(this);
		this.killServers.addActionListener(this);

		this.getContentPane().add(tabbedPane);

		this.getContentPane().add(toolBar, BorderLayout.PAGE_START);

		this.stopJButton.setEnabled(false);

		this.buildMenuBar();

		this.addWindowListener(this);
	}

	public Mapper getMapper() {
		return mapper;
	}

	public void setMapper(Mapper mapper) {
		this.mapper = mapper;
	}

	public GeneratorsController getGeneratorsController() {
		return generatorsController;
	}

	public void setGeneratorsController(GeneratorsController generatorsController) {
		this.generatorsController = generatorsController;
	}

	public static void main(String[] args) {
		MainFrame frame = new MainFrame();
		frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		frame.setSize(1024, 786);
		frame.setTitle("McMaster Grid Scheduling Testing Environment");

		// Centering the frame
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		frame.setLocation(screenSize.width / 2 - frame.getWidth() / 2, screenSize.height / 2 - frame.getHeight() / 2);

		frame.setVisible(true);
	}

	/**
	 * This method builds the MenuBar
	 */
	public void buildMenuBar() {
		// Create the menu bar
		menuBar = new JMenuBar();

		// Set this instance as the application's menu bar
		setJMenuBar(menuBar);

		/*----- Build the Action menu ------*/
		menuAction = new JMenu("Action");
		menuAction.setMnemonic('A');

		menuItemStart = createMenuItem(menuAction, ITEM_PLAIN, "Start", null, 'S', null);
		menuItemStop = createMenuItem(menuAction, ITEM_PLAIN, "Stop", null, 'P', null);
		menuItemFailureTraces = createMenuItem(menuAction, ITEM_PLAIN, "Fill Failure Traces", null, 'F', null);
		menuItemProcessingRates = createMenuItem(menuAction, ITEM_PLAIN, "Fill Processing Rates", null, 'R', null);

		menuItemStop.setEnabled(false);
		menuBar.add(menuAction);

		/*----- Build the Tables menu ------*/
		menuTables = new JMenu("Tables");
		menuTables.setMnemonic('T');

		menuItemSaveDefinitions = createMenuItem(menuTables, ITEM_PLAIN, "Export Definitons", null, 'S', null);
		menuItemSaveDefinitions.setToolTipText("Use this option to export the machine definitions, execution rates and job class definitions.");
		
		menuItemLoadDefinitions = createMenuItem(menuTables, ITEM_PLAIN, "Import Definitions", null, 'L', null);
		menuItemLoadDefinitions.setToolTipText("Use this option to import the machine definitions, execution rates and job class definitions.");
		
		menuTables.addSeparator();

		menuItemSaveSystemStats = createMenuItem(menuTables, ITEM_PLAIN, "Export Generic Statistics", null, 'Y', null);
		menuItemSaveSystemStats.setToolTipText("Use this option to export generic statistics such as Start Time, End Time, Scheduling Scheme and the average Waiting Time.");
		
		menuItemSaveJobClassesStats = createMenuItem(menuTables, ITEM_PLAIN, "Export Job Classes Statistics", null, 'C', null);
		menuItemSaveJobClassesStats.setToolTipText("Use this option to export statistics about job classes such as Number of Jobs, Arrival Rate and Actual Arrival Rate.");
		
		menuItemSaveMueMatrix = createMenuItem(menuTables, ITEM_PLAIN, "Export Execution Rates Statistics", null, 'M', null);
		menuItemSaveMueMatrix.setToolTipText("Use this option to export statistics about the execution rates of server for each job class.");
		
		menuItemSaveJobTable = createMenuItem(menuTables, ITEM_PLAIN, "Export Job Table", null, 'J', null);
		menuItemSaveJobTable.setToolTipText("Use this option to export a table that has statistics about each individual job.");
		
		menuTables.addSeparator();
		menuItemSaveAll = createMenuItem(menuTables, ITEM_PLAIN, "Export All", null, 'A', null);
		menuItemSaveAll.setToolTipText("Use this option to export the following: Generic Statistics, Job Classes Statistics, Execution Rates and Job Table.");
		menuBar.add(menuTables);

		/*----- Build the Puller menu ------*/
		menuServers = new JMenu("Servers");
		menuServers.setMnemonic('S');

		menuItemStartServers = createMenuItem(menuServers, ITEM_PLAIN, "Start Servers", null, 'S', null);
		menuItemPingServers = createMenuItem(menuServers, ITEM_PLAIN, "Ping Servers", null, 'N', null);
		menuItemPauseServers = createMenuItem(menuServers, ITEM_PLAIN, "Pause Servers", null, 'P', null);
		menuItemKillServers = createMenuItem(menuServers, ITEM_PLAIN, "Kill Servers", null, 'K', null);
		menuBar.add(menuServers);

		/*----- Build the Help menu ------*/
		menuHelp = new JMenu("Help");
		menuHelp.setMnemonic('H');
		menuItemManual = createMenuItem(menuHelp, ITEM_PLAIN, "User Manual", null, 'A', null);
		menuItemThesis = createMenuItem(menuHelp, ITEM_PLAIN, "Thesis Document", null, 'A', null);
		menuBar.add(menuHelp);

		/*----- Build the About menu ------*/
		menuAbout = new JMenu("About");
		menuAbout.setMnemonic('A');
		menuItemAbout = createMenuItem(menuAbout, ITEM_PLAIN, "About", null, 'H', null);
		menuBar.add(menuAbout);

	}

	/**
	 * This method is used by the method buildMenuBar() to create and initialize
	 * menuItems
	 */
	public JMenuItem createMenuItem(JMenu menu, int iType, String sText, ImageIcon image, int acceleratorKey,
			String sToolTip) {
		// Create the item
		JMenuItem menuItem;

		switch (iType) {
		case ITEM_RADIO:
			menuItem = new JRadioButtonMenuItem();
			break;

		case ITEM_CHECK:
			menuItem = new JCheckBoxMenuItem();
			break;

		default:
			menuItem = new JMenuItem();
			break;
		}

		// Add the item test
		menuItem.setText(sText);

		// Add the optional icon
		if (image != null)
			menuItem.setIcon(image);

		// Add the accelerator key
		if (acceleratorKey > 0)
			menuItem.setMnemonic(acceleratorKey);

		// Add the optional tool tip text
		if (sToolTip != null)
			menuItem.setToolTipText(sToolTip);

		// Add an action handler to this menu item
		menuItem.addActionListener(this);

		menu.add(menuItem);

		return menuItem;
	}

	/**
	 * Builds the default Job Classes table.
	 */
	public void buildJobClasses() throws IOException {
		
		String currentLine;
		
		int endofAR;
		String ar, iter;
		double ArrivalRate;
		long executionTime;
		this.setReadFile("genericclasses.txt"); //Default file is genericclasses.txt
		JobClassesTable table = new JobClassesTable();
		BufferedReader file = new BufferedReader(new FileReader(this.getReadFile()));
		
		try
		{
			while((currentLine = file.readLine()) != null)
			{
				endofAR = 0;
				
				while (currentLine.charAt(endofAR++) != ';') {}
				
				ar = currentLine.substring(0, endofAR - 1).trim();
				ArrivalRate = Double.parseDouble(ar);
				iter = currentLine.substring(endofAR).trim();
				executionTime = Long.parseLong(iter);
				table.addJobClass(ArrivalRate, executionTime);
			}
			this.getMapper().setClassesTable(table);
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * Builds the servers table.
	 */
	public void buildServers() {
		ServersTable serversTable = new ServersTable();
		try {
			ServersReader.readServersFromFile("Servers/servers.srs", serversTable);
		} catch (Exception ex) {
			this.getMapper().setServersTable(new ServersTable());
			return;
		}
		this.getMapper().setServersTable(serversTable);
		this.getMapper().fillProcessingRates();
	}

	/**
	 * This method creates a MainFrame object. It is invoked by the OpeningFrame
	 */
	public static MainFrame creatMainFrame() {
		MainFrame frame = new MainFrame();
		frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		frame.setSize(1024, 786);
		frame.setTitle("McMaster Grid Scheduling Testing Environment");

		// Centering the frame
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		frame.setLocation(screenSize.width / 2 - frame.getWidth() / 2, screenSize.height / 2 - frame.getHeight() / 2);

		return frame;
	}

	/**
	 * To start the jobs generators
	 */
	private void startGenerators() {
		this.getGeneratorsController().startGenerators();
	}

	/**
	 * To stop the jobs generators
	 */
	private void stopGenerators() {
		this.getGeneratorsController().stopGenerators();
	}

	/**
	 * This method starts the operation of generating and scheduling
	 */
	private void start() {
		this.getMapper().startMapper();
		this.startGenerators();
		this.availabilityServer.startServer();
		this.completionServer.startServer();

		savingRunnable.startThread();

		this.startJButton.setEnabled(false);
		this.menuItemStart.setEnabled(false);
		this.stopJButton.setEnabled(true);
		this.menuItemStop.setEnabled(true);
		this.saveJButton.setEnabled(false);
		this.loadJButton.setEnabled(false);
		this.fillProcessingRate.setEnabled(false);
		this.failureTraceButton.setEnabled(false);
		this.serversTableJPanel.disableInput();
		this.jobClassesTableJPanel.disableInput();
		this.jobClassesJPanel.setEnabled(true);
		this.availableServersJPanel.setModelForJTable(this.getMapper().getAvailableServersQueue());
	}

	/**
	 * This method stops the operation of generating and scheduling
	 */
	private void stop() {
		this.getMapper().stopMapper();
		this.stopGenerators();
		this.availabilityServer.stopServer();
		this.completionServer.stopServer();

		this.startJButton.setEnabled(true);
		this.menuItemStart.setEnabled(true);
		this.stopJButton.setEnabled(false);
		this.menuItemStop.setEnabled(false);
		this.saveJButton.setEnabled(true);
		this.loadJButton.setEnabled(true);
		this.fillProcessingRate.setEnabled(true);
		this.failureTraceButton.setEnabled(true);
		this.serversTableJPanel.enableInput();
		this.jobClassesTableJPanel.enableInput();

		this.savingRunnable.stopThread();
		// Stop Servers
	}

	/**
	 * This method is used to start sending starting messages that prepares the Server objects to
	 * receive tasks. It must be called to run the servers
	 */
	public void sendStartMessages() {
		Socket mapperSocket = null;
		DataOutputStream out = null;
		int port = 37934;

		int i = 0;

		int size = this.mapper.getServersTable().size();
		ProgressBarFrame progressFrame = new ProgressBarFrame("Servers Started", size);
		for (i = 0; i < size; i++) {
			try {
				mapperSocket = new Socket(this.mapper.getServer(i + 1).getHostName(), port);

				out = new DataOutputStream(mapperSocket.getOutputStream());
				String s = "start#" + this.mapper.getTimeResolution() + "#" + this.mapper.getMapperHostName() + "#"
						+ this.getMapper().getServer(i + 1).getLastMessageSent().toString();
				System.out.print("Message: " + s);
				out.writeBytes(s);
				out.flush();
				progressFrame.changeProgress(i + 1, "Server " + (i + 1) + " started successfully.");
				progressFrame.repaint();
				System.out.println("sendStartMessages: " + s);
			} catch (ConnectException e) {
				progressFrame.changeProgress(i + 1, "Server " + (i + 1)
						+ " refuses the connection. Puller software might not be working.");
			} catch (UnknownHostException e1) {
				progressFrame.changeProgress(i + 1, "Server " + (i + 1) + " is Unknownhost.");
			} catch (IOException e1) {
				progressFrame.changeProgress(i + 1, "Server " + (i + 1) + " IOException.");
			} finally {
				if (mapperSocket != null)
					try {
						mapperSocket.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				if (out != null) {
					try {
						out.close();
						out.flush();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	/**
	 * This method sends a ping signal to all servers to check if servers are
	 * working.
	 */
	public void sendPingMessages() {
		Socket mapperSocket = null;
		DataOutputStream out = null;
		int port = 37934;

		int i = 0;

		int size = this.mapper.getServersTable().size();
		ProgressBarFrame progressFrame = new ProgressBarFrame("Servers Ping", size);
		for (i = 0; i < size; i++) {
			try {
				mapperSocket = new Socket();// new
											// Socket(this.mapper.getServer(i +
											// 1).getHostName().trim(), port);
				SocketAddress server = new InetSocketAddress(this.mapper.getServer(i + 1).getHostName().trim(), port);
				try {
					mapperSocket.connect(server, 2000);
				} catch (SocketTimeoutException ex) {
					progressFrame.changeProgress(i + 1, "Server (" + this.mapper.getServer(i + 1).getHostName().trim()
							+ ") timed out the connection.");
					continue;
				}
				out = new DataOutputStream(mapperSocket.getOutputStream());
				String s = "ping";
				out.writeBytes(s);
				out.flush();
				progressFrame.changeProgress(i + 1, "Server " + this.mapper.getServer(i + 1).getHostName().trim()
						+ " Pinged successfully.");
			} catch (ConnectException e) {
				progressFrame.changeProgress(i + 1, "Server (" + this.mapper.getServer(i + 1).getHostName().trim()
						+ ") refuses the connection. Puller software might not be working.");
				e.printStackTrace();
			} catch (UnknownHostException e1) {
				progressFrame.changeProgress(i + 1, "Server " + (i + 1) + " is Unknownhost.");
			} catch (IOException e1) {
				progressFrame.changeProgress(i + 1, "Server " + (i + 1) + " IOException.");
			} finally {
				if (mapperSocket != null)
					try {
						mapperSocket.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				if (out != null) {
					try {
						out.close();
						out.flush();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	/**
	 * This method sends a restart signal to all servers.
	 */
	public void sendRestartMessages() {
		Socket mapperSocket = null;
		DataOutputStream out = null;
		int port = 37934;

		int i = 0;

		int size = this.mapper.getServersTable().size();
		ProgressBarFrame progressFrame = new ProgressBarFrame("Servers Restart", size);
		for (i = 0; i < size; i++) {
			try {
				mapperSocket = new Socket(this.mapper.getServer(i + 1).getHostName(), port);
				out = new DataOutputStream(mapperSocket.getOutputStream());
				String s = "restart";
				out.writeBytes(s);
				out.flush();
				progressFrame.changeProgress(i + 1, "Server " + (i + 1) + " paused successfully.");
			} catch (ConnectException e) {
				progressFrame.changeProgress(i + 1, "Server " + (i + 1)
						+ " refuses the connection. Puller software might not be working.");
				e.printStackTrace();
			} catch (UnknownHostException e1) {
				progressFrame.changeProgress(i + 1, "Server " + (i + 1) + " is Unknownhost.");
			} catch (IOException e1) {
				progressFrame.changeProgress(i + 1, "Server " + (i + 1) + " IOException.");
			} finally {
				if (mapperSocket != null)
					try {
						mapperSocket.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				if (out != null) {
					try {
						out.close();
						out.flush();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	/**
	 * This method sends a kill signal to all servers.
	 */
	public void sendStopMessages() {
		Socket mapperSocket = null;
		DataOutputStream out = null;
		int port = 37934;
		int i = 0;
		int size = this.mapper.getServersTable().size();
		ProgressBarFrame progressFrame = new ProgressBarFrame("Servers Stop", size);
		for (i = 0; i < size; i++) {
			try {
				mapperSocket = new Socket(this.mapper.getServer(i + 1).getHostName(), port);
				out = new DataOutputStream(mapperSocket.getOutputStream());
				String s = "stop";
				out.writeBytes(s);
				out.flush();
				progressFrame.changeProgress(i + 1, "Server " + (i + 1) + " stopped successfully.");
			} catch (ConnectException e) {
				progressFrame.changeProgress(i + 1, "Server " + (i + 1)
						+ " refuses the connection. Puller software might not be working.");
				e.printStackTrace();
			} catch (UnknownHostException e1) {
				progressFrame.changeProgress(i + 1, "Server " + (i + 1) + " is Unknownhost.");
			} catch (IOException e1) {
				progressFrame.changeProgress(i + 1, "Server " + (i + 1) + " IOException.");
			} finally {
				if (mapperSocket != null)
					try {
						mapperSocket.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				if (out != null) {
					try {
						out.close();
						out.flush();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	/**
	 * This method handles all the GUI signals like clicking on the start button
	 * or save buttons
	 */
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == startJButton || e.getSource() == this.menuItemStart) {
			start();
			return;
		}

		if (e.getSource() == stopJButton || e.getSource() == this.menuItemStop) {
			stop();
			return;
		}

		if (e.getSource() == saveJButton || e.getSource() == this.menuItemSaveDefinitions) {
			JFileChooser chooser = new JFileChooser();
			int returnVal = chooser.showSaveDialog(this);
			if (returnVal != JFileChooser.APPROVE_OPTION)
				return;
			this.getMapper().saveMapperAsObjects(chooser.getSelectedFile().getAbsolutePath() + ".def");
			return;
		}

		if (e.getSource() == loadJButton || e.getSource() == this.menuItemLoadDefinitions) {
			FileFilter filter = new ExtensionFileFilter("DEF", new String[] { "DEF" });
			JFileChooser chooser = new JFileChooser();
			chooser.setFileFilter(filter);
			int returnVal = chooser.showOpenDialog(this);
			if (returnVal != JFileChooser.APPROVE_OPTION)
				return;

			this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

			this.getMapper().readMapperAsObjects(chooser.getSelectedFile().getAbsolutePath());
			this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
			
			this.serversTableJPanel.fillJTable();
			this.serversTableJPanel.showServerInfo();
			this.jobClassesTableJPanel.fillJTable();
			this.serversAvailabilityPanel.fillJTable();
			this.systemLevelParametersJPanel.refreshValues();

			this.getMapper().getServersTable().addServersListener(this.serversTableJPanel);
			this.getMapper().getJobsTable().addJobsTableListener(jobsTableJPanel);

			return;
		}

		if (e.getSource() == printMapperJButton || e.getSource() == this.menuItemPrint) {
			
			this.getMapper().printAvailabilityQueue();
			System.out.println("-------------");

			this.getMapper().printServers();
			System.out.flush();
			System.err.flush();

			this.getMapper().print();
			return;
		}

		if (e.getSource() == failureTraceButton || e.getSource() == this.menuItemProcessingRates) {

			this.getMapper().fillAllFailureTraces(this.getMapper().getFailurePeriodsMean(),
					this.getMapper().getUpTimePeriodsMean());

			this.serversTableJPanel.showServerInfo();
			return;
		}

		if (e.getSource() == this.fillProcessingRate || e.getSource() == this.menuItemFailureTraces) {
			this.mapper.fillProcessingRates();
			this.serversTableJPanel.showServerInfo();
			return;
		}

		if (e.getSource() == this.startServers || e.getSource() == this.menuItemStartServers) {
			this.sendStartMessages();
			return;
		}

		if (e.getSource() == this.menuItemPingServers) {
			this.sendPingMessages();
			return;
		}

		if (e.getSource() == this.pauseServers || e.getSource() == this.menuItemPauseServers) {
			this.sendRestartMessages();
			return;
		}

		if (e.getSource() == this.killServers || e.getSource() == this.menuItemKillServers) {
			this.sendStopMessages();
			return;
		}

		// Used to open a pdf file bundled with the jar file of this application.
		if (e.getSource() == this.menuItemManual) {

			String command = null;
			
			if (System.getProperty("os.name").equalsIgnoreCase("Mac OS X")) 
			{
				command = "open " + "./" + "userManual.pdf";
			}
			if (System.getProperty("os.name").startsWith("Windows"))
			{
				command = "\"userManual.pdf\"";
			}
			
			Runtime runtime = Runtime.getRuntime();
			try 
			{
				runtime.exec(command);
			} 
			catch (Exception ex) 
			{
				ex.printStackTrace();
			}

		}
		
		// Used to open a pdf file bundled with the jar file of this application.
		if (e.getSource() == this.menuItemThesis) {
			
			String command = null;
			
			if (System.getProperty("os.name").equalsIgnoreCase("Mac OS X")) 
			{
				command = "open ./thesis.pdf";
			}
			if (System.getProperty("os.name").startsWith("Windows"))
			{
				command = "\"thesis.pdf\"";
			}
			
			Runtime runtime = Runtime.getRuntime();
			try 
			{
				runtime.exec(command);
			} 
			catch (Exception ex) 
			{
				ex.printStackTrace();
			}
		}

		if (e.getSource() == this.menuItemAbout) {
			JOptionPane
					.showMessageDialog(
							this,
							"McMaster Grid Scheduling Testing Environment\nŠ June 2008, McMaster University, Copyrights reserved",
							"MGST", JOptionPane.INFORMATION_MESSAGE);
		}

		if (e.getSource() == this.menuItemSaveSystemStats) {
			JFileChooser fileChooser = new JFileChooser();
			int returnedValue = fileChooser.showSaveDialog(this);
			if (returnedValue != JFileChooser.APPROVE_OPTION) {
				return;
			}
			Reporter.generateReportAboutSystemStatistics(this.getMapper(), fileChooser.getSelectedFile()
					.getAbsolutePath());
		}

		if (e.getSource() == this.menuItemSaveJobClassesStats) {
			JFileChooser fileChooser = new JFileChooser();
			int returnedValue = fileChooser.showSaveDialog((this));
			if (returnedValue != JFileChooser.APPROVE_OPTION) {
				return;
			}
			try {
				Reporter.generateReportAboutJobClasses(this.getMapper(), this.getGeneratorsController(), fileChooser
						.getSelectedFile().getAbsolutePath());
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}

		if (e.getSource() == this.menuItemSaveMueMatrix) {
			JFileChooser fileChooser = new JFileChooser();
			int returnedValue = fileChooser.showSaveDialog((this));
			if (returnedValue != JFileChooser.APPROVE_OPTION) {
				return;
			}
			try {
				Reporter.generateReportOfMue(this.getMapper(), fileChooser.getSelectedFile().getAbsolutePath());
			} catch (IOException e1) {
				JOptionPane.showMessageDialog(this, "Could not save!", "Could not save!", JOptionPane.ERROR_MESSAGE);
			}
		}

		if (e.getSource() == menuItemSaveJobTable) {
			JFileChooser fileChooser = new JFileChooser();
			int returnVal = fileChooser.showSaveDialog(this);
			if (returnVal != JFileChooser.APPROVE_OPTION)
				return;
			this.getMapper().getJobsTable().saveToFile(fileChooser.getSelectedFile().getAbsolutePath());
		}

		if (e.getSource() == this.menuItemSaveAll) {
			JFileChooser fileChooser = new JFileChooser();
			int returnedValue = fileChooser.showSaveDialog((this));
			if (returnedValue != JFileChooser.APPROVE_OPTION) {
				return;
			}
			try {
				Reporter.generateReportAboutSystemStatistics(this.getMapper(), fileChooser.getSelectedFile()
						.getAbsolutePath()
						+ "_systemStats.txt");
				Reporter.generateReportAboutJobClasses(this.getMapper(), this.getGeneratorsController(), fileChooser
						.getSelectedFile().getAbsolutePath()
						+ "_classesStats.txt");
				Reporter.generateReportOfMue(this.getMapper(), fileChooser.getSelectedFile().getAbsolutePath()
						+ "_mue.txt");
				this.getMapper().getJobsTable().saveToFile(
						fileChooser.getSelectedFile().getAbsolutePath() + "_jobs.txt");
			} catch (IOException e1) {
				JOptionPane.showMessageDialog(this, "Could not save!", "Could not save!", JOptionPane.ERROR_MESSAGE);
			}
		}
	}

	public void windowActivated(WindowEvent e) {
		// TODO Auto-generated method stub

	}

	public void windowClosed(WindowEvent e) {
		this.setVisible(true);

	}

	public void windowClosing(WindowEvent e) {
		int result = JOptionPane.showConfirmDialog(this, "Are you sure?", "Exit", JOptionPane.YES_NO_OPTION,
				JOptionPane.YES_NO_OPTION);
		if (result == JOptionPane.YES_OPTION) {
			System.exit(1);
		}

		this.setSize(1024, 786);
		this.setTitle("McMaster Grid Scheduling Testing Environment (MGST)");

		this.setVisible(true);

	}

	public void windowDeactivated(WindowEvent e) {
		// TODO Auto-generated method stub

	}

	public void windowDeiconified(WindowEvent e) {
		// TODO Auto-generated method stub

	}

	public void windowIconified(WindowEvent e) {
		// TODO Auto-generated method stub

	}

	public void windowOpened(WindowEvent e) {
		// TODO Auto-generated method stub

	}

}
