
package com.engineous.system.drm.ldlev;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.util.Map;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

import com.engineous.common.gui.ETextField;
import com.engineous.common.i18n.IString;
import com.engineous.sdk.exception.SDKException;
import com.engineous.sdk.pse.DRMOptionsPanel;
import com.engineous.sdk.resmgr.ResMgr;


/**
 * This class implements the DRM Enabler resource
 * selection editor panel for Load Leveler.
 */
public class LoadLevelerOptionsPanel
	extends JPanel
		implements DRMOptionsPanel {

	private static final Class<LoadLevelerOptionsPanel> CLASS = LoadLevelerOptionsPanel.class;

	private JLabel lblJobName = new JLabel();
	private ETextField txtJobName = new ETextField(30);
	private JLabel lblClass = new JLabel();
	private ETextField txtClass = new ETextField();
	private JLabel lblAccount = new JLabel();
	private ETextField txtAccount = new ETextField();
	private JLabel lblParallelTasks = new JLabel();
	private ETextField txtParallelTasks = new ETextField();
	private JLabel lblMaxMemory = new JLabel();
	private ETextField txtMaxMemory = new ETextField();
	private JLabel lblStartDate = new JLabel();
	private ETextField txtStartDate = new ETextField();
	private JLabel lblRunLimit = new JLabel();
	private JTextField txtRunLimit = new ETextField();
	private JLabel lblWorkingDir = new JLabel();
	private ETextField txtWorkingDir = new ETextField();

	private Map<String,String> ldlevResources = null;

	/**
	 * Constructor for LoadLevelerOptionsPanel objects.  All DRM Options
	 * panel classes are packaged as 'Editor' classes in DRM metamodels,
	 * and so are required to have a default constructor.
	 */
	public LoadLevelerOptionsPanel() {
		super(new GridBagLayout());

		lblJobName.setText(ResMgr.getMessage(CLASS, 0, "Job Name"));
		txtJobName.setEditable(true);

		lblClass.setText(ResMgr.getMessage(CLASS, 0, "Class"));
		txtClass.setEditable(true);

		lblAccount.setText(ResMgr.getMessage(CLASS, 0, "Account"));
		txtAccount.setEditable(true);

		lblParallelTasks.setText(ResMgr.getMessage(CLASS, 0, "Number of Parallel Tasks"));
		txtParallelTasks.setEditable(true);

		lblMaxMemory.setText(ResMgr.getMessage(CLASS, 0, "Memory ( <60000 / 60GB)"));
		txtMaxMemory.setEditable(true);

		lblStartDate.setText(ResMgr.getMessage(CLASS, 0, "Start Date ([[YYYY:]MM:]DD)"));
		txtStartDate.setEditable(true);

		lblRunLimit.setText(ResMgr.getMessage(CLASS, 0, "Wall Clock Limit ([hh:]mm)"));
		txtRunLimit.setEditable(true);

		lblWorkingDir.setText(ResMgr.getMessage(CLASS, 0, "Working Directory"));
		txtWorkingDir.setEditable(true);

		Insets labelInsets = new Insets(3, 5, 2, 5);
		Insets textInsets = new Insets(3, 0, 2, 5);

		add(lblJobName, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, labelInsets, 0, 0));
		add(txtJobName, new GridBagConstraints(1, 0, 3, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, textInsets, 0, 0));
		add(lblClass, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, labelInsets, 0, 0));
		add(txtClass, new GridBagConstraints(1, 1, 3, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, textInsets, 0, 0));
		add(lblAccount, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, labelInsets, 0, 0));
		add(txtAccount, new GridBagConstraints(1, 2, 3, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, textInsets, 0, 0));
		add(lblParallelTasks, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, labelInsets, 0, 0));
		add(txtParallelTasks, new GridBagConstraints(1, 3, 3, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, textInsets, 0, 0));
		add(lblMaxMemory, new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, labelInsets, 0, 0));
		add(txtMaxMemory, new GridBagConstraints(1, 4, 3, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, textInsets, 0, 0));
		add(lblStartDate, new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, labelInsets, 0, 0));
		add(txtStartDate, new GridBagConstraints(1, 5, 3, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, textInsets, 0, 0));
		add(lblRunLimit, new GridBagConstraints(0, 7, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, labelInsets, 0, 0));
		add(txtRunLimit, new GridBagConstraints(1, 7, 3, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, textInsets, 0, 0));
		add(lblWorkingDir, new GridBagConstraints(0, 8, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, labelInsets, 0, 0));
		add(txtWorkingDir, new GridBagConstraints(1, 8, 3, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, textInsets, 0, 0));

		cancel();  // Clears the GUI
	}

	/**
	 * This method will configure the panel to show all DRM resources
	 * described by the given String, which must have been generated
	 * by the DRMEnabler method 'getDRMResourceSet()', and set the
	 * panel state so that none of the resources has been selected.
	 * Implementors of this class are responsible for defining the
	 * resource-set representation; if the given String does not con-
	 * form to that representation this method must throw an SDKEx-
	 * ception.
	 * <p>
	 * When the user raises the Isight resource selection GUI, this
	 * text shall be fetched from the DRM installation in the Simulia
	 * Execution Engine and passed into the panel via this method.
	 * @param drmResources String
	 * @throws SDKException
	 */
	public void setDRMResourceSelection(String drmResources)
			throws SDKException {
		// NOTHING TO CONFIGURE
	}

	/**
	 * This method sets a configured panel to a state in which any
	 * resources defined by the current DRMResourceSelection object
	 * may be selected, as specified by the given text; if an empty
	 * string is given, the panel must be set to a state in which
	 * no resources are selected.  Implementors of this class are
	 * responsible for defining the selection-set representation;
	 * if the given String does not conform to that representation
	 * this method must throw an SDKException.
	 * <p>
	 * When the user raises the Isight resource selection GUI, this
	 * text shall be extracted from a property of the edited compo-
	 * nent and passed into the panel via this method.
	 * @param selectionText String
	 * @throws SDKException
	 */
	public void setSelectedDRMResources(String selectionText)
			throws SDKException {

		// Process the selection text into Load Leveler resource settings:
		ldlevResources = LoadLevelerUtils.fromString(selectionText);

		// Display the settings:
		txtJobName.setText(getResource(LoadLevelerUtils.LDLEV_RESOURCE_JOB_NAME));
		txtClass.setText(getResource(LoadLevelerUtils.LDLEV_RESOURCE_CLASS));
		txtAccount.setText(getResource(LoadLevelerUtils.LDLEV_RESOURCE_ACCOUNT));
		txtParallelTasks.setText(getResource(LoadLevelerUtils.LDLEV_RESOURCE_PARALLEL_TASKS));
		txtMaxMemory.setText(getResource(LoadLevelerUtils.LDLEV_RESOURCE_MAX_MEMORY));
		txtStartDate.setText(getResource(LoadLevelerUtils.LDLEV_RESOURCE_START_DATE));
		txtRunLimit.setText(getResource(LoadLevelerUtils.LDLEV_RESOURCE_RUN_LIMIT));
		txtWorkingDir.setText(getResource(LoadLevelerUtils.LDLEV_RESOURCE_WORKING_DIR));
	}

	/**
	 * This method collects all of the resource selection data from
	 * the panel and returns a String which encodes the selection.
	 * The panel may return an empty string when no resources have
	 * been selected.  Implementors of this class are responsible
	 * for defining the selection-set representation.
	 * <p>
	 * When the user raises the Isight resource selection GUI, this
	 * text shall be extracted from the panel via this method and
	 * stored in a property of the edited component.
	 * @returns String
	 */
	public String getSelectedDRMResources() {

		setResource(LoadLevelerUtils.LDLEV_RESOURCE_JOB_NAME,txtJobName.getText());
		setResource(LoadLevelerUtils.LDLEV_RESOURCE_CLASS,txtClass.getText());
		setResource(LoadLevelerUtils.LDLEV_RESOURCE_ACCOUNT,txtAccount.getText());
		setResource(LoadLevelerUtils.LDLEV_RESOURCE_PARALLEL_TASKS,txtParallelTasks.getText());
		setResource(LoadLevelerUtils.LDLEV_RESOURCE_MAX_MEMORY,txtMaxMemory.getText());
		setResource(LoadLevelerUtils.LDLEV_RESOURCE_START_DATE,txtStartDate.getText());
		setResource(LoadLevelerUtils.LDLEV_RESOURCE_RUN_LIMIT,txtRunLimit.getText());
		setResource(LoadLevelerUtils.LDLEV_RESOURCE_WORKING_DIR,txtWorkingDir.getText());

		// Process the LSF resource settings into selection text:
		return LoadLevelerUtils.toString(ldlevResources);
	}

	/**
	 * If the contents of the panel are valid as a resource selection
	 * subset, this method must return null; if not, it must return
	 * a String, which the caller displays as a message identifying
	 * the invalid selection field (or fields).  Isight will not call
	 * 'getSelectedDRMResources()' to retrieve the selection unless
	 * this method first returns null.
	 * <p>
	 * The Isight resource selection GUI will call this method
	 * in response to user clicks on an editor dialog's 'OK'
	 * and/or 'Apply' buttons.
	 * @return String
	 */
	public String validateOptions() {

		String numTasks = txtParallelTasks.getText().trim();
		if (numTasks.length() > 0) {
			if ( !isLBNumber(numTasks,1)) {
				return new IString(CLASS,0,"Task count \"{0}\" is not a positive number.",numTasks).getMessage();
			}
		}

		String maxMemory = txtMaxMemory.getText().trim();
		if (maxMemory.length() > 0) {
			if ( !isLBNumber(maxMemory,1)) {
				return new IString(CLASS,0,"Maximum memory \"{0}\" is not a positive number.",numTasks).getMessage();
			}
		}

		String startDate = txtStartDate.getText().trim();
		if (startDate.length() > 0) {
			boolean isOK = true;
			String[] startDateTerms = startDate.split(":");
			if (startDateTerms.length <= 3) {  // DD, MM:DD, or YYYY:MM:DD
				for (String timeUnit : startDateTerms) {
					isOK &= isLBNumber(timeUnit.trim(),1);
				}
			}
			else {
				isOK = false;
			}

			if ( !isOK ) {
				return new IString(CLASS,0,"Value \"{0}\" does not fit the format of {1}.", startDate, lblStartDate.getText()).getMessage();
			}
		}

		String runLimit = txtRunLimit.getText().trim();
		if (runLimit.length() > 0) {
			boolean isOK = true;
			String[] runLimitTerms = runLimit.split(":");
			if (runLimitTerms.length <= 2) {
				isOK &= isLBNumber(runLimitTerms[0],1);  // 'mm' OR 'hh:mm'
				if (runLimitTerms.length > 1) {
					isOK &= isLBNumber(runLimitTerms[1],0);  // 'hh:mm'
				}
			}
			else {
				isOK = false;
			}

			if ( !isOK ) {
				return new IString(CLASS,0,"Value \"{0}\" does not fit the format of {1}.", runLimit, lblRunLimit.getText()).getMessage();
			}
		}

		return null;
	}

	/**
	 * This method returns true iff the given string
	 * represents an integer no smaller than N.
	 * @param valStr String
	 * @param N int
	 * @return boolean
	 */
	private boolean isLBNumber(String valStr, int N) {

		try {
			int val = Integer.parseInt(valStr);
			return (val >= N);
		}
		catch (NumberFormatException nfe) {
			return false;
		}
	}

	/**
	 * This method will reset the panel to a state in which no
	 * DRM resources have been selected [note: this allows one
	 * instance of the panel to be reused].
	 * <p>
	 * The Isight resource selection GUI will call this method
	 * in response to user clicks on an editor dialog's 'OK'
	 * or 'Cancel' buttons.
	 */
	public void cancel() {
		txtJobName.setText("");
		txtClass.setText("");
		txtAccount.setText("");
		txtParallelTasks.setText("");
		txtMaxMemory.setText("");
		txtStartDate.setText("");
		txtRunLimit.setText("");
		txtWorkingDir.setText("");
	}

	/**
	 * This method returns the resource setting value recorded in
	 * the specified field of the Load Leveler resource selection
	 * panel.
	 * @param resourceName String
	 * @return String
	 */
	private String getResource(String resourceName) {

		if ((ldlevResources != null) && ldlevResources.containsKey(resourceName)) {
			return ldlevResources.get(resourceName);
		}
		else {
			return "";
		}
	}

	/**
	 * This method sets the resource setting value recorded in
	 * the specified field of the Load Leveler resource selection
	 * panel to the given value.
	 * @param resourceName String
	 * @param resourceValue String
	 */
	private void setResource(String resourceName, String resourceValue) {

		if ((ldlevResources != null) && ldlevResources.containsKey(resourceName)) {
			if (resourceValue != null) {
				ldlevResources.put(resourceName,resourceValue.trim());
			}
			else {
				ldlevResources.put(resourceName,"");
			}
		}
	}
}
