/* Copyright Dassault Systemes, 1999, 2009 */


package examples.development.datatypes;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;

import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JPanel;

import com.engineous.common.i18n.IString;
import com.engineous.desktop.sdk.AbstractValueEditor;
import com.engineous.desktop.sdk.ValueRenderer;
import com.engineous.sdk.gui.ExceptionHandler;
import com.engineous.sdk.vars.Value;
import com.engineous.sdk.vars.ValueChangeEvent;
import com.engineous.sdk.vars.Variable;
import com.engineous.sdk.vars.VariableException;

/**
 * Example editor and renderer for the Complex Value class.
 * This editor behaves like the editor for the RealValue class - it displays 'real,imag' when viewing,
 * and replaces this with 2 RealNumberTextFields when actually editing.
 * <p>
 * It might have been better to behave like the StringValue editor - when editing, display the text with a "..."
 * button to open a larger edit panel.  Or to simply allow editing of 'real,imag' as text and report an error if
 * the syntax is wrong.
 */
public class ComplexEditor
		extends AbstractValueEditor
		implements ValueRenderer {

	// Declaration required by Internationalization tool - used in IString constructor and ResMgr.getMessage
	transient private static final Class CLASS = ComplexEditor.class;

	/** Label used to render the value when it is not being edited. Not created until needed. */
	private JLabel renderLabel;

	/** Panel used to display and edit the value when it IS being edited.  Not created until needed. */
	private JPanel editPanel;

	// Parts of the editPanel
	private JFormattedTextField realField, imagField;
	private JLabel plusLabel, jLabel;

	/** The Value object being edited.  Is set in getEditorUI and used in apply() */
	private ComplexValue editValue;
	
	
	
	private FocusListener focusListener = new FocusAdapter() {

		public void focusLost(FocusEvent ve) {
			//   Don't do anything when focus isn't 'really' lost.
			//   [otherwise you get spurious duplicate actions]
			if (ve.isTemporary()) {
				return;
			}

			apply();
		}
	};

	/**
	 * Required no-argument constructor.
	 */
	public ComplexEditor() {
		// Nothing to do.  Everything is created in getEditorUI and getRendererUI.
	}

	/**
	 * Get the UI used to render the value.  The returned object MAY be editable, but it
	 * will never actually be edited, as it is replaced by the result of getEditorUI as soon as the table
	 * cell is selected.  If it is not editable, it should NOT show as "disabled" as that makes tables look gray.
	 * @param value Value to display.
	 * @param owner The Variable that owns the Value.  May be ScalarVariable or ArrayVariable.
	 * @return The JComponent that will display the value, initialized for the correct value.
	 * @see com.engineous.desktop.sdk.ValueRenderer#getRendererUI(com.engineous.sdk.vars.Value, com.engineous.sdk.vars.Variable)
	 */
	public JComponent getRendererUI(Value value, Variable owner) {

		if (renderLabel == null) {
			renderLabel = new JLabel();
		}
		
		if (value != null)
		{
			ComplexValue cvalue = (ComplexValue)value;
			renderLabel.setText(cvalue.getRe() + " + " + cvalue.getIm() + " j");
		}		
		return renderLabel;

	}

	/**
	 * Get the UI used to edit the value.  The returned component must contain an editable component (JTextField
	 * or something like that.  'apply' will be called to store the updated value back into the Value object, so it
	 * has to be saved.
	 * @param value
	 * @param owner
	 * @return
	 * @see com.engineous.desktop.sdk.ValueEditor#getEditorUI(com.engineous.sdk.vars.Value, com.engineous.sdk.vars.Variable)
	 */
	public JComponent getEditorUI(Value value, Variable owner) {

		if (editPanel == null) {
			createEditPanel();
		}
		if (value == null) {
			try {
				editValue = new ComplexValue();
			}
			catch (VariableException ex) {
				// This should never happen.  If it does, give up.
				throw new Error("Cannot create ComplexValue", ex);
			}
		}
		else {
			editValue = (ComplexValue)value;
		}
		realField.setValue(new Double(editValue.getRe()));
		imagField.setValue(new Double(editValue.getIm()));
		return editPanel;

	}

	/**
	 * Get the Value object currently being edited.
	 * @return The Value object being edited, AFTER it has been updated.
	 * @see com.engineous.desktop.sdk.ValueEditor#getValue()
	 */
	public Value getValue() {
		return editValue;
	}

	/**
	 * Called to apply any changes in the editor to the Value object.
	 * @see com.engineous.desktop.sdk.AbstractValueEditor#apply()
	 */
	public void apply() {

		super.apply();
		if (editValue == null) {
			// this can happen, and it means there is nothing we can do.
			return;
		}
		if (editPanel == null) {
			// this should not happen.  If it does, report it and hope it doesn't happen again.

			// Note:  Class IString is part of the Isight Internationalization framework.  It is
			// used to provide translated versions of strings based on an integer message number.
			// Using IString for translation requires a SIMULIA-internal tool that assigns unique 
			// message numbers and builds message catalogs for translation.  However, many Isight
			// classes expect an IString as an argument.  To work around this, the special message
			// number -1 means "do not translate this string, do not assign a unique message number".
			// Customer code that must construct IStrings to pass to methods that only accept an 
			// IString (such as many exception classes), should use the form shown below to build
			// the IString without the translation tool.
			ExceptionHandler.showError(editPanel, null, new IString(CLASS, -1, "ComplexEditor.apply without getEditorUI"),
					new IString(CLASS, -1, "Error"));
			return;
		}
		Double newRe, newIm;
		try {
			realField.commitEdit();
			imagField.commitEdit();
			newRe = (Double)realField.getValue();
			newIm = (Double)imagField.getValue();
			
			if ((newRe.doubleValue() == editValue.getRe()) && (newIm.doubleValue() == editValue.getIm())) {
				fireValueEditingComplete();
				return;
			}
			
			
			Value oldeditValue = (Value)editValue.clone();
			editValue.setValue(newRe.doubleValue(), newIm.doubleValue());
			fireValueChanged(new ValueChangeEvent(editValue, oldeditValue));
		}
		catch (Exception ex) {
			ExceptionHandler.showError(editPanel, ex, new IString(CLASS, -1, "Unable to set ComplexValue"), new IString(CLASS, -1, "Error"));
		}
	}

	

	/**
	 * Create the editor GUI
	 */
	public void createEditPanel() {

		// Create the entries.
		editPanel = new JPanel(new GridBagLayout());
		realField = new JFormattedTextField();
		realField.setBorder(null);
		imagField = new JFormattedTextField();
		imagField.setBorder(null);
		plusLabel = new JLabel("+");
		jLabel = new JLabel("j");

		// merge them.
		editPanel.add(realField,
					  new GridBagConstraints(0, 0, 1, 1, 1.0, 0.5, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0,
						  0));
		editPanel.add(plusLabel,
					  new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 1, 0, 1), 0, 0));
		editPanel.add(imagField,
					  new GridBagConstraints(2, 0, 1, 1, 1.0, 0.5, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0,
						  0));
		editPanel.add(jLabel,
					  new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 1, 0, 1), 0, 0));
		realField.addFocusListener(focusListener);
		imagField.addFocusListener(focusListener);

	}
	
	@Override
	public void dispose() {
		super.dispose();
		realField.removeFocusListener(focusListener);
		imagField.removeFocusListener(focusListener);
	}

}

