/*****************************************************************************

@header@
@date@
@copyright@
@license@

*****************************************************************************/

/*
HTML code for running the applet:
<applet code="org/nfunk/jepexamples/Evaluator.class" width=400 height=200>
</applet>
*/
package com.singularsys.jepexamples.applets;

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;

import com.singularsys.jep.*;
import com.singularsys.jep.standard.StandardVariableTable;


/**
 * This applet is an simple example for how JEP can be used to evaluate
 * expressions. It also displays the different options, and the effects of
 * their settings.
 */
public class Evaluator extends Applet {

	private static final long serialVersionUID = 4592714713689369505L;

	/** Parser */
	private Jep jep;
	
	/** Current xValue */
	private double xValue;

	/* GUI components */
	private TextField exprField, xField;
	private TextArea errorTextArea;
	private Label resultLabel;
	private Checkbox implicitCheckbox, undeclaredCheckbox;

	
	/** 
	 * This method is called if the applet is run as an standalone
	 * program. It creates a frame for the applet and adds the applet
	 * to that frame.
	 */
	public static void main(String args[]) {
		Evaluator a = new Evaluator();
		a.init();
		a.start();

		Frame f = new Frame("Evaluator");
		f.add("Center", a);
		f.setSize(400,200);
		f.addWindowListener(
			new WindowAdapter() {
				public void windowClosing(WindowEvent e) {
					System.exit(0);
				}
			}
		);
		
//		f.show(); 
		f.setVisible(true);
	}

	/**
	 * The initialization function of the applet. It adds all the
	 * components such as text fields and also creates the JEP object
	 */
	public void init() {
		// initialize value for x
		xValue = 10;

		// add the interface components
		addGUIComponents();
		
		// Set up the parser (more initialization in parseExpression()) 
		jep = new Jep();
		
		// simulate changed options to initialize output
		optionsChanged();
	}

	/**
	 * Creates and adds the necessary GUI components.
	 */
	private void addGUIComponents() {
		setBackground(Color.white);
		
		GridBagLayout gridbag = new GridBagLayout();
		GridBagConstraints c = new GridBagConstraints();
		setLayout(gridbag);

		// Expression
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 0.0;
		Label exprFieldp = new Label("Expression: ", Label.RIGHT);
		gridbag.setConstraints(exprFieldp,c);
		add(exprFieldp);
		
		c.weightx = 0.8;
		exprField = new TextField(27);
		gridbag.setConstraints(exprField,c);
		add(exprField);
		
		// x
		c.weightx = 0.0;
		Label xFieldp = new Label("x: ", Label.RIGHT);
		gridbag.setConstraints(xFieldp,c);
		add(xFieldp);
		
		c.weightx = 0.2;
		c.gridwidth = GridBagConstraints.REMAINDER;
		xField = new TextField("" + xValue,4);
		gridbag.setConstraints(xField,c);
		add(xField);
		
		// Result
		c.weightx = 0.0;
		c.gridwidth = 1;
		Label resultLabelText = new Label("Result: ", Label.RIGHT);
		gridbag.setConstraints(resultLabelText,c);
		add(resultLabelText);
		
		c.weightx = 1.0;
		c.gridwidth = GridBagConstraints.REMAINDER;
		resultLabel = new Label("", Label.LEFT);
		gridbag.setConstraints(resultLabel,c);
		add(resultLabel);
		
		// Options
		c.weightx = 0.0;
		c.gridwidth = 1;
		Label optionsLabelText = new Label("Options: ", Label.RIGHT);
		gridbag.setConstraints(optionsLabelText,c);
		add(optionsLabelText);
		
		c.weightx = 1.0;
		c.gridwidth = GridBagConstraints.REMAINDER;
		implicitCheckbox = new Checkbox("Implicit multiplication", true);
		gridbag.setConstraints(implicitCheckbox,c);
		add(implicitCheckbox);
		
		c.weightx = 0.0;
		c.gridwidth = 1;
		Label spaceLabelText = new Label(" ", Label.RIGHT);
		gridbag.setConstraints(spaceLabelText,c);
		add(spaceLabelText);

		c.weightx = 1.0;
		c.gridwidth = GridBagConstraints.REMAINDER;
		undeclaredCheckbox = new Checkbox("Allow undeclared identifiers");
		gridbag.setConstraints(undeclaredCheckbox,c);
		add(undeclaredCheckbox);

		// Errors
		c.weightx = 0.0;
		c.gridwidth = 1;
		c.anchor = GridBagConstraints.NORTH;
		Label errorLabel = new Label("Errors: ", Label.RIGHT);
		gridbag.setConstraints(errorLabel,c);
		add(errorLabel);
		
		c.fill = GridBagConstraints.BOTH;
		c.weightx = 1.0;
		c.weighty = 1.0;
		c.gridwidth = GridBagConstraints.REMAINDER;
		errorTextArea = new TextArea("");
		errorTextArea.setEditable(false);
		errorTextArea.setBackground(Color.white);
		gridbag.setConstraints(errorTextArea,c);
		add(errorTextArea);

		// Set up listeners
		exprField.addTextListener(
			new TextListener() {
				public void textValueChanged(TextEvent evt) {
					exprFieldTextValueChanged();
				}
			}
		);

		xField.addTextListener(
			new TextListener() {
				public void textValueChanged(TextEvent evt) {
					xFieldTextValueChanged();
				}
			}
		);
		
		implicitCheckbox.addItemListener(
			new ItemListener() {
				public void itemStateChanged(ItemEvent evt) {
					optionsChanged();
				}
			}
		);
		
		undeclaredCheckbox.addItemListener(
			new ItemListener() {
				public void itemStateChanged(ItemEvent evt) {
					optionsChanged();
				}
			}
		);
	}

	/**
	 * Parses the current expression in the exprField. This method also
	 * re-initializes the contents of the symbol and function tables. This
	 * is necessary because the "allow undeclared variables" option adds
	 * variables from expressions to the symbol table.
	 */
	private boolean parseExpression() {
		// reload the standard variable table (with pi, e, and i)
		jep.setComponent(new StandardVariableTable(jep.getVariableFactory()));
		// add the x variable
		jep.addVariable("x", xValue);
		// try parsing
		try {
			jep.parse(exprField.getText());
			exprField.setBackground(new Color(255, 255, 255));
			return true;
		} catch (ParseException e) {
			exprField.setBackground(new Color(255, 220, 220));
			resultLabel.setText("");
			errorTextArea.setText("Error while parsing:\n"+e.getMessage());
		}
		return false;
	}

	/**
	 * Whenever the expression is changed, this method is called.
	 * The expression is parsed, and the updateResult() method
	 * invoked.
	 */
	private void exprFieldTextValueChanged() {
		if (parseExpression()) updateResult();
	}
	
	/**
	 * Every time the value in the x field is changed, this method is
	 * called. It takes the value from the field as a double, and
	 * sets the value of x in the parser.
	 */
	private void xFieldTextValueChanged() {
		
		try {
			xValue = Double.valueOf(xField.getText()).doubleValue();
		} catch (NumberFormatException e) {
			System.out.println("Invalid format in xField");
			xValue = 0;
		}

		jep.addVariable("x", xValue);

		if (parseExpression()) updateResult();
	}
	
	/**
	 * Every time one of the options is changed, this method is called. The
	 * parser settings are adjusted to the GUI settings, the expression is
	 * parsed again, and the results updated.
	 */
	private void optionsChanged() {
		jep.setImplicitMul(implicitCheckbox.getState());
		jep.setAllowUndeclared(undeclaredCheckbox.getState());
		if (parseExpression()) updateResult();
	}
	
	/**
	 * This method uses JEP's getValueAsObject() method to obtain the current
	 * value of the expression entered.
	 */
	private void updateResult() {
		Object result = null;
		
		// Get the value
		try {
			result = jep.evaluate();
			
			// Is the result ok?
			if (result!=null) {
				resultLabel.setText(result.toString());
			} else {
				resultLabel.setText("null");
			}
			errorTextArea.setText("");
		} catch (EvaluationException e) {
			// Clear the result and print the error message
			resultLabel.setText("");
			errorTextArea.setText("Error while evaluating:\n"+e.getMessage());
		}
	}
}
