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

/**
 * UI for animation editor
 */
public class MorphViewer extends JPanel implements BehaviorDisplayListener
{
  private BehaviorDisplay displayA = null;
  private BehaviorDisplay displayB = null;
  private BehaviorDisplay animation = null;
  
  // for animation evaluation
  private ELEnvironment environment = null;
  double oldInterpValue = 0.0;
  
  private int frameWidth = 64;
  private int frameHeight = 64;

  private int animationFrameWidth = 200;
  private int animationFrameHeight = 200;

  double morphInterpolationParameter = 0.0;
  
  public MorphViewer()
  {
    displayA = new BehaviorDisplay(new Behavior(new ELScalar(0), frameWidth, frameHeight, 0));
    displayA.setBehaviorDisplayListener(this);
    displayB = new BehaviorDisplay(new Behavior(new ELScalar(0), frameWidth, frameHeight, 0));
    displayB.setBehaviorDisplayListener(this);
    animation = new BehaviorDisplay(new Behavior(new ELScalar(0), animationFrameWidth, animationFrameHeight, 0));
    animation.setBehaviorDisplayListener(this);

    environment = new ELEnvironment();

    JSlider slider = new JSlider();
    slider.setModel(new DoublePercentageFieldAdapter(this, "morphInterpolationParameter"));
    slider.addChangeListener(new ChangeListener() {
        public void stateChanged(ChangeEvent e) {
          // JSlider sends us a state changed event when the slider is
          // first clicked on.  Don't respond to that one, since
          // nothing has changed yet.
          if (oldInterpValue != morphInterpolationParameter) {
            environment.setMorphInterpolationParameter(morphInterpolationParameter);
            MorphViewer.this.evaluate();
            oldInterpValue = morphInterpolationParameter;
          }
        }
      });

    GridBagLayout gridBag = new GridBagLayout();
    GridBagConstraints constraints = new GridBagConstraints();
    setLayout(gridBag);

    constraints.weightx = 1.0;
    constraints.weighty = 1.0;

    constraints.gridwidth = GridBagConstraints.REMAINDER;
    gridBag.setConstraints(animation, constraints);
    add(animation);

    constraints.gridwidth = 1;
    gridBag.setConstraints(displayA, constraints);
    add(displayA);

    constraints.gridwidth = GridBagConstraints.RELATIVE;
    constraints.fill = GridBagConstraints.HORIZONTAL;
    constraints.weightx = 4.0;
    gridBag.setConstraints(slider, constraints);
    add(slider);

    constraints.fill = GridBagConstraints.NONE;
    constraints.weightx = 1.0;
    constraints.gridwidth = GridBagConstraints.REMAINDER;
    gridBag.setConstraints(displayB, constraints);
    add(displayB);
  }

  /**
   * Forces reevaluation of the animation.
   */
  void evaluate()
  {
    animation.behavior.dirty = true;

    long startTime = System.currentTimeMillis();

    animation.behavior.evaluate(environment);

    long deltaTime = System.currentTimeMillis() - startTime;
    //System.out.println(" " + deltaTime + "ms");

    animation.repaint();
  }

  // inherited from BehaviorDisplayListener
  public void behaviorDisplayChange(BehaviorDisplay b)
  {
    if (b != animation) {
      // Create the morph expression
      ELExpression expression = 
        Evolver.instance.geneticParameters.createGeneticMorphExpression(displayA.behavior.expression, 
                                                                        displayB.behavior.expression);
      
      // Change the animation 
      animation.setBehavior(new Behavior(expression, animationFrameWidth, animationFrameHeight, 0));
    }
  }  
}
