package interfacing;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;

import mapping.FCFS_MS;
import mapping.Gcu_MS;
import mapping.LPAS_DG_MS;
import mapping.MET_MS;
import mapping.SLQ_MS;
import mapping.NREP_MS;

/**
 * This JPanel is responsible for inserting or changing system level parameters
 * that include: "Time Units in minutes", "Mean Time to Repair", "Mean Time to Fail"
 * Length Mean", "Mapping Scheme", "Time Resolution", "Artificial failures and
 * T/O" and "Time-out after "
 * 
 * @author Majd Kokaly
 * 
 */
public class SystemLevelParametersJPanel extends JPanel implements ActionListener, AncestorListener {

	/**
	 * 
	 */
	private static final long serialVersionUID = 2968259505414678075L;

	/**
	 * The ComboBox of all the schemes that the GUI is aware of.
	 */
	private JComboBox mappingSchemeCB = new JComboBox();

	/**
	 * LPAS index in the mappingSchemeCB JComboBox
	 */
	private final static int LPAS_DG = 0;

	/**
	 * GCU index in the mappingSchemeCB JComboBox
	 */
	private final static int GCU = 1;

	/**
	 * mappingSchemeCB index in the mappingSchemeCB JComboBox
	 */
	private final static int MET = 2;

	/**
	 * FCFS index in the mappingSchemeCB JComboBox
	 */
	private final static int FCFS = 3;
	
	/**
	 * SLQ index in the mappingSchemeCB JComboBox
	 */
	private final static int SLQ = 4;

	/**
	 * NREP index in the mappingSchemeCB JComboBox
	 */
	private final static int NREP = 5;
	
	/**
	 * The JButton that is clicked to apply input
	 */
	private JButton applyBN = new JButton("Apply");

	/**
	 * An array of labels that label the properties showed in this JPanel
	 */
	private String[] labels = { "Minutes Per Time Units", "Mean Time to Repair", "Mean Time to Fail", "Mapping Scheme",
			"Time Resolution", "Artificial Failures", "Time-out After (times)" };


	/**
	 * An array of tips that label the properties showed in this JPanel
	 */
	
	private String[] tips = { 
			//"Minutes Per Time Units"
			"In MGST, instead of using minutes or seconds, Time Units are used. This field defines how many minutes are in one time unit. For example, if 2.0 was set, then every time unit in the actual testing will take 2 minutes.",
			
			//"Mean Time to Repair"
			"This value determines the mean of the failure period used to generate artificial traces.",
			
			//"Mean Time to Fail"
			"This value determines the mean of the up time periods used to generate artificial traces.",
			
			// "Mapping Scheme"
			"The Mapping Scheme to be used in the test.",
			
			// "Time Resolution"
			"This time determintes the period after which the puller sends an update for the mapper. ", 

			// "Artificial Failures"
			"This determines if failures are simulated or not.", 
			
			//Time-out After (times)"
			"To declare a job as timed-out one, a time of (n * (time esitmated for The Job To be completed)) must elapse. This paremeter determines n"
			
			};


	
	
	/**
	 * This is the JPanel containing the properties and their value.
	 */
	private PropertiesJPanel propPanel;

	/**
	 * The MainFrame that is contains this JPanel
	 */
	private MainFrame frame;

	/**
	 * Default constructor
	 * 
	 * @param frame
	 *            to be set to the frame field
	 */
	public SystemLevelParametersJPanel(MainFrame frame) {

		this.frame = frame;

		propPanel = new PropertiesJPanel(labels, tips);

		mappingSchemeCB.addItem(new String("LPAS_DG"));
		mappingSchemeCB.addItem(new String("Gcu"));
		mappingSchemeCB.addItem(new String("MET"));
		mappingSchemeCB.addItem(new String("FCFS"));
		mappingSchemeCB.addItem(new String("SLQ"));
		//mappingSchemeCB.addItem(new String("NREP"));

		propPanel.replaceValueComponent(3, mappingSchemeCB);
		propPanel.replaceValueComponent(5, new JCheckBox());


		applyBN.addActionListener(this);

		this.setLayout(new GridBagLayout());
		GridBagConstraints gbConstraints = new GridBagConstraints();
		gbConstraints.anchor = GridBagConstraints.CENTER;
		gbConstraints.gridx = 0;
		gbConstraints.gridy = 0;
		this.add(propPanel, gbConstraints);
		gbConstraints.gridx = 0;
		gbConstraints.gridy = 1;
		this.add(applyBN, gbConstraints);

		// This segment forces the north alignment of the components
		gbConstraints.weighty = 1.0;
		gbConstraints.fill = GridBagConstraints.BOTH;
		gbConstraints.gridy = 2;
		this.add(javax.swing.Box.createVerticalGlue(), gbConstraints);

		this.addAncestorListener(this);

	}

	/**
	 * A flag used in method hasUserModified
	 */
	boolean firstTime = true;

	/**
	 * The repaint() method is used to refresh the Values().
	 */
	public void repaint() {
		if (propPanel == null)
			return;

		if (this.hasUserModified()) {
			return;
		}

		refreshValues();
	}

	/**
	 * This method reads the values from the Mapper object and show them in the
	 * JPanel
	 */
	public void refreshValues() {
		DecimalFormat df = new DecimalFormat();
		df.setMaximumFractionDigits(2);

		try {
			propPanel.setValue(0, this.frame.getMapper().getTimeUnitInMinutes() + "");
			propPanel.setValue(1, df.format(this.frame.getMapper().getFailurePeriodsMean()) + "");
			propPanel.setValue(2, df.format(this.frame.getMapper().getUpTimePeriodsMean()) + "");
			propPanel.setValue(3, this.frame.getMapper().getMappingScheme().getDescription());

			propPanel.setValue(4, this.frame.getMapper().getTimeResolution() + "");
			propPanel.setValue(5, this.frame.getMapper().isTimeOutActive() + "");
			propPanel.setValue(6, this.frame.getMapper().getTimeOutFactor() + "");

		} catch (NullPointerException ex) {
			ex.printStackTrace();
			propPanel.setValue(0, "0");
			propPanel.setValue(1, "0");
			propPanel.setValue(2, "0");
			propPanel.setValue(3, "0");
		}
	}

	/**
	 * This method is invoked with the applyJButton is clicked.
	 */
	public void actionPerformed(ActionEvent e) {
		applyInput();
	}

	/**
	 * This method reads the fields filled by the user and set the values in the
	 * Mapper accordingly.
	 */
	public void applyInput() {
		String[] values = propPanel.getValues();


		double timeUnit = 0;
		double periodMean = 0.0;
		double interPeriodsMean = 0.0;
		int timeResolution = 0;
		boolean aritfitialFailurAndTimeOutsActive = false;
		double timeOutFactor = 1;

		try {
			timeUnit = Double.parseDouble(values[0]);
		} catch (NumberFormatException ex) {
			JOptionPane.showMessageDialog(frame, "Time Unit should be a number.", "Error", JOptionPane.ERROR_MESSAGE);
			return;
		}

		try {
			periodMean = Double.parseDouble(values[1]);
		} catch (NumberFormatException ex) {
			JOptionPane
					.showMessageDialog(frame, labels[1] + " should be a number.", "Error", JOptionPane.ERROR_MESSAGE);
			return;
		}

		try {
			interPeriodsMean = Double.parseDouble(values[2]);
		} catch (NumberFormatException ex) {
			JOptionPane
					.showMessageDialog(frame, labels[2] + " should be a number.", "Error", JOptionPane.ERROR_MESSAGE);
			return;
		}

		// What Mapping Scheme to use
		switch (mappingSchemeCB.getSelectedIndex()) {
		case LPAS_DG:
			this.frame.getMapper().setMappingScheme(new LPAS_DG_MS(this.frame.getMapper()));
			break;
		case GCU:
			this.frame.getMapper().setMappingScheme(new Gcu_MS(this.frame.getMapper()));
			break;
		case MET:
			this.frame.getMapper().setMappingScheme(new MET_MS(this.frame.getMapper()));
			break;
		case FCFS:
			this.frame.getMapper().setMappingScheme(new FCFS_MS(this.frame.getMapper()));
			break;
		case SLQ:
			this.frame.getMapper().setMappingScheme(new SLQ_MS(this.frame.getMapper()));
			break;
		case NREP:
			this.frame.getMapper().setMappingScheme(new NREP_MS(this.frame.getMapper()));
			break;
		}

		try {
			timeResolution = Integer.parseInt(values[4]);
		} catch (NumberFormatException ex) {
			JOptionPane.showMessageDialog(frame, "Time resolution should be an integer.", "Error",
					JOptionPane.ERROR_MESSAGE);
			return;
		}

		if (values[5].equalsIgnoreCase("true")) {
			aritfitialFailurAndTimeOutsActive = true;
		}

		if (aritfitialFailurAndTimeOutsActive) {
			try {
				timeOutFactor = Double.parseDouble(values[6]);
				if (timeOutFactor < 1.5) {
					JOptionPane.showMessageDialog(frame, "Timeout fold should be a number larger or equal 1.5.",
							"Error", JOptionPane.ERROR_MESSAGE);
					return;
				}
			} catch (NumberFormatException ex) {
				JOptionPane.showMessageDialog(frame, "Timeout fold should be a number.", "Error",
						JOptionPane.ERROR_MESSAGE);
				return;
			}
		}

		frame.getMapper().setTimeUnitInMinutes(timeUnit);
		frame.getMapper().setTimeResolution(timeResolution);
		frame.getMapper().setFailurePeriodsMean(periodMean);
		frame.getMapper().setUpTimePeriodsMean(interPeriodsMean);
		frame.getMapper().fillAllFailureTraces(periodMean, interPeriodsMean);
		frame.getMapper().setTimeOutActive(aritfitialFailurAndTimeOutsActive);
		frame.getMapper().setArtificialFailuresActive(aritfitialFailurAndTimeOutsActive);
		frame.getMapper().setTimeOutFactor(timeOutFactor);

	}

	public void ancestorAdded(AncestorEvent arg0) {

	}

	public void ancestorMoved(AncestorEvent arg0) {

	}

	/**
	 * This method is invoked when the use click on something else than this
	 * JPanel If the user has modified some properties and did not apply them,
	 * this method issues a confirmation message.
	 */
	public void ancestorRemoved(AncestorEvent arg0) {
		// System.out.println("modified: " + this.propPanel.beenModified);
		if (this.hasUserModified()) {
			this.frame.serversAndJobsClassesTablesTabbedPane.setSelectedIndex(0);
			this.frame.tabbedPane.setSelectedIndex(0);
			int result = JOptionPane.showConfirmDialog(this,
					"You have modified the parameters, do you want to apply changes?", "Apply",
					JOptionPane.YES_NO_OPTION);
			if (result == JOptionPane.YES_OPTION) {
				applyInput();
			}
			if (result == JOptionPane.NO_OPTION) {
				refreshValues();
			}

		}

	}

	/**
	 * This method is used to warns the users in the case of modifying some
	 * properties and not applying them
	 * 
	 * @return true if user has modified some properties without applying then
	 *         and false otherwise.
	 */

	public boolean hasUserModified() {

		if (firstTime) {
			firstTime = false;
			return false;
		}


		if (Double.parseDouble(propPanel.getValue(0)) != this.frame.getMapper().getTimeUnitInMinutes()) {
			return true;
		}

		if (Double.parseDouble(propPanel.getValue(1)) != this.frame.getMapper().getFailurePeriodsMean())
			return true;

		if (Double.parseDouble(propPanel.getValue(2)) != this.frame.getMapper().getUpTimePeriodsMean())
			return true;

		if (!propPanel.getValue(3).equalsIgnoreCase(this.frame.getMapper().getMappingScheme().getDescription()))
			return true;

		if (Double.parseDouble(propPanel.getValue(4)) != (this.frame.getMapper().getTimeResolution()))
			return true;

		if (!(frame.getMapper().isTimeOutActive() + "").equalsIgnoreCase(propPanel.getValue(5))) {
			return true;
		}

		if (frame.getMapper().isTimeOutActive())
			if (Double.parseDouble(propPanel.getValue(6)) != this.frame.getMapper().getTimeOutFactor()) {
				return true;
			}

		return false;

	}

}
