/// Evolver Copyright 2000, Morgan McGuire
/// matrix@alum.mit.edu   http://graphics3d.com/matrix
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.border.*;
import javax.swing.event.*;
import java.net.*;

/**
 * UI for viewing a single behavior.  Invoke the main function to
 * bring up independently of Evolver.
 */
public class BehaviorViewer extends JPanel implements BehaviorDisplayListener
{
    GeneticParameters geneticParameters;

    boolean animate = true;
    JTextArea expressionDisplay;
    BehaviorDisplay display;
    ELExpressionGraph graph;

    JButton okButton = null;
    JButton cancelButton = null;

    /**
     * Expression before we started editing.
     */
    private String oldExpression = "";

    /**
     * True when the expression is being edited.
     */
    private boolean editingExpression = false;

    public BehaviorViewer()
    {
	this(Evolver.instance.geneticParameters);
    }

    static public void main(String arg[]) 
    {
	GeneticParameters gp = new GeneticParameters(".");
	JFrame frame = new JFrame("Evolver Interpreter");
	frame.getContentPane().add(new BehaviorViewer(gp));
	frame.setIconImage(new ImageIcon("bv-icon.jpg").getImage());
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	frame.pack();
	frame.setVisible(true);
    }

    public BehaviorViewer(GeneticParameters gp) 
    {
	this.geneticParameters = gp;
	//super(JSplitPane.VERTICAL_SPLIT);
    
	setBackground(new Color(181, 181, 181));

	JPanel topPane = new JPanel();

	expressionDisplay = new JTextArea();
	expressionDisplay.setLineWrap(true);
	expressionDisplay.setWrapStyleWord(true);

	expressionDisplay.setFont(new Font("Arial", Font.PLAIN, 16));
	expressionDisplay.setMargin(new Insets(14, 14, 14, 14));
	expressionDisplay.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));

	// If the display is selected and the user clicks again, 
	expressionDisplay.addMouseListener(new MouseInputAdapter() {
	    public void mouseClicked(MouseEvent e) 
		{
		    BehaviorViewer bv = BehaviorViewer.this;
		    if (! bv.editingExpression && bv.expressionDisplay.hasFocus()) {
			bv.setEditingExpression(true);
		    }
		}
	});

	expressionDisplay.addFocusListener(new FocusAdapter() {
	    public void focusLost(FocusEvent e) 
		{
		    BehaviorViewer bv = BehaviorViewer.this;
		    if (bv.editingExpression) {
			bv.commitExpression();
		    }
		}
	});

	JPanel editPanel = createEditPanel();

	setEditingExpression(false);

	int d = 128;
	Behavior behavior = new Behavior(new ELScalar(0), d, d, 0);

	graph = new ELExpressionGraph();

	JPanel displayPanel = new JPanel();
	displayPanel.setLayout(new BoxLayout(displayPanel, BoxLayout.Y_AXIS));

	display = new BehaviorDisplay(behavior, 3.0);
	display.setBehaviorDisplayListener(this);
	display.startAnimation();

	JCheckBox animateToggle = new JCheckBox("Animate");
	animateToggle.setSelected(true);
	animateToggle.setModel(new BooleanFieldAdapter(this, "animate"));
	animateToggle.addChangeListener(new ChangeListener() {
	    public void stateChanged(ChangeEvent event) {
		if (animate) {
		    display.startAnimation();
		} else {
		    display.stopAnimation();
		}
	    }
	});
	displayPanel.add(display);
	

	final JComboBox resolutionCombo = 
	    new JComboBox(new Object[]{"96x96",
				       "128x128",
				       "256x256",
				       "128x128*3",
				       "384x384",
				       "512x512",
				       "640x480",
				       "800x600"});
	resolutionCombo.setEditable(false);
	resolutionCombo.addActionListener(new ActionListener() {
	    public void actionPerformed(ActionEvent event) {
		String res = (String)resolutionCombo.getSelectedItem();
		int scale = 1;
		if (res.endsWith("*3")) {
		    scale = 3;
		    res = res.substring(0, res.length() - 2);
		}
		int mid = res.indexOf('x');
		int width = Integer.parseInt(res.substring(0, mid));
		int height = Integer.parseInt(res.substring(mid + 1, res.length()));
		display.setBehavior(new Behavior(display.behavior.expression, width, height, true), scale);
	    }
	});
	{
	    JPanel panel = new JPanel();
	    panel.add(animateToggle);
	    panel.add(resolutionCombo);
	    displayPanel.add(panel);
	}
    
	topPane.setLayout(new BoxLayout(topPane, BoxLayout.X_AXIS));
	topPane.add(displayPanel);
	topPane.add(graph);
    
	setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
	add(topPane);
	add(editPanel);
	resolutionCombo.setSelectedIndex(3);
    }

    private JPanel createEditPanel() 
    {
	ImageIcon okIcon = null;
	ImageIcon cancelIcon = null;
	try {
	    okIcon = new ImageIcon(new URL("file:ok.gif"));
	    cancelIcon = new ImageIcon(new URL("file:cancel.gif"));
	} catch (MalformedURLException e) {
	    System.err.println("Warning: " + e);
	}

	okButton = new JButton(okIcon);
	cancelButton = new JButton(cancelIcon);
	okButton.setFocusPainted(false);
	cancelButton.setFocusPainted(false);

	cancelButton.addActionListener(new ActionListener() {
	    public void actionPerformed(ActionEvent e) {
		BehaviorViewer bv = BehaviorViewer.this;
		bv.expressionDisplay.setText(bv.oldExpression);
		bv.setEditingExpression(false);
	    }
	});

	GridBagLayout gbl = new GridBagLayout();
	GridBagConstraints constraints = new GridBagConstraints();

	JPanel editPanel = new JPanel();
	editPanel.setLayout(gbl);
	constraints.gridheight = 2;
	constraints.gridwidth = GridBagConstraints.RELATIVE;
	constraints.weightx = 1.0;
	constraints.fill = GridBagConstraints.BOTH;

	gbl.setConstraints(expressionDisplay, constraints);
	editPanel.add(expressionDisplay);

	constraints.fill = GridBagConstraints.NONE;
	constraints.gridheight = 1;
	constraints.gridwidth = GridBagConstraints.REMAINDER;
	gbl.setConstraints(okButton, constraints);
	editPanel.add(okButton);

	gbl.setConstraints(cancelButton, constraints);
	editPanel.add(cancelButton);

	return editPanel;
    }

    /**
     * Causes this viewer to display a new behavior.  
     */
    public void setBehavior(Behavior behavior)
    {
	if (! display.behavior.expression.unparse().equals(display.behavior.expression.unparse())) {
	    setEditingExpression(false);
	    expressionDisplay.setText(behavior.expression.unparse());
	    display.setBehavior(behavior);
	}
    }

    public void behaviorDisplayChange(BehaviorDisplay b) 
    {
	setEditingExpression(false);
	expressionDisplay.setText(display.behavior.expression.unparse());
	graph.setExpression(display.behavior.expression);
    }

    /**
     * Loads the edited expression as the new expression.
     */
    void commitExpression()
    {
	setEditingExpression(false);

	String expressionString = expressionDisplay.getText();

	// Create an expression
	ELExpression expr = null;
	try {
	    expr = geneticParameters.parseELExpression(expressionString, false);
	} catch (ParseError e) {
	    popupDialog("Parse Error", "While parsing '" + expressionString + "': " + e.toString());
	}

	if (expr == null) {
	    System.err.println("Can't parse expression '" + expressionString + "'");
	    expressionDisplay.setText(oldExpression);
	} else {
	    display.setExpression(expr);
	}    
    }
  
    /**
     * Flips the expression display gui.
     */
    void setEditingExpression(boolean state) {
	if (state) {
	    // Back up the expression
	    oldExpression = expressionDisplay.getText();

	    expressionDisplay.setForeground(java.awt.Color.black);        
	    expressionDisplay.setEditable(true);
	    expressionDisplay.setBackground(java.awt.Color.white);

	    expressionDisplay.getCaret().setVisible(true);
	    expressionDisplay.setCaretColor(Color.black);
	    okButton.setEnabled(true);
	    cancelButton.setEnabled(true);
	} else {
	    expressionDisplay.getCaret().setVisible(false);
	    expressionDisplay.setForeground(new java.awt.Color(61,61,61));
	    expressionDisplay.setEditable(false);
	    expressionDisplay.setBackground(new java.awt.Color(153, 196,167));
	    okButton.setEnabled(false);
	    cancelButton.setEnabled(false);
	}
	editingExpression = state;
    }

    public void update()
    {
    }

    /**
     * Makes a popup dialog
     */
    static private void popupDialog(String title, String m) 
    {
	final JDialog myDialog = new JDialog();

	JTextArea text = new JTextArea(m);
	text.setLineWrap(true);
	text.setWrapStyleWord(true);
	text.setEditable(false);
	Action closeAction = new AbstractAction() {
	    public void actionPerformed(ActionEvent event) {
		myDialog.dispose();
	    }};

	closeAction.putValue(Action.NAME, "Close");

	JPanel panel = new JPanel();
	text.setBackground(panel.getBackground());
	text.setBorder(new EmptyBorder(5,5,5,5));
	panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
	panel.add(text);
	panel.setMinimumSize(new Dimension(400,200));

	JButton closeButton = new JButton(closeAction);
	closeButton.setAlignmentX(JButton.CENTER_ALIGNMENT);
	panel.add(closeButton);

	myDialog.getContentPane().add(panel);

	myDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
	myDialog.setModal(true);
	myDialog.setTitle(title);
	myDialog.pack();

	myDialog.setVisible(true);	    
    }
}

