/// Evolver Copyright 2000, Morgan McGuire
/// matrix@alum.mit.edu   http://graphics3d.com/matrix
/**
 * An interpolator for animation
 */
public class ELMorphInterpolator extends AbstractELExpression implements ELExpression
{
  ELExpression a;
  ELExpression b;

  /**
   * a and b will not be cloned.
   */
  public ELMorphInterpolator(ELExpression a, ELExpression b)
  {
    this.a = a;
    this.b = b;
  }

  public ELExpression geneticClone(ELCall parent, GeneticParameters gp)
  {
    throw new Error("Cannot clone an expression containing a Morph Interpolator");
  }

  public Object clone()
  {
    return new ELMorphInterpolator((ELExpression)a.clone(), (ELExpression)b.clone());
  }
  
  public String unparse()
  {
    return "MorphInterpolator(" + a.unparse() + ", " + b.unparse() + ")";
  }

  public String toString()
  {
    return "ELMorphInterpolator(...)";
  }

  public void evaluate(ELMatrix out, final ELEnvironment environment, final ParameterList pl)
  {
    final ELMatrix aResult = ELMatrix.alloc(out.getWidth(), out.getHeight());
    ELMatrix bResult = out;

    if (environment.multiThreadInterpolation) {
      // Evaluate each
      Thread aThread = new Thread() {
          public void run() {
	      ELMorphInterpolator.this.a.evaluate(aResult, environment, pl);
          }};
      
      aThread.start();
      b.evaluate(bResult, environment, pl);
      
      // Wait for second thread to complete
      if (aThread.isAlive()) {
        try {
          aThread.join();
        } catch (InterruptedException e) {
          throw new Error(e.toString());
        }
      }

    } else {

      a.evaluate(aResult, environment, pl);
      b.evaluate(bResult, environment, pl);

    }

    // Combine the results using linear interpolation
    float bCoeff = (float)environment.getMorphInterpolationParameter();
    float aCoeff = 1.0f - bCoeff;
    
    float outChannelr[] = out.channel[0];
    float aChannelr[] = aResult.channel[0];
    float bChannelr[] = bResult.channel[0];
    
    float outChannelg[] = out.channel[1];
    float aChannelg[] = aResult.channel[1];
    float bChannelg[] = bResult.channel[1];
    
    float outChannelb[] = out.channel[2];
    float aChannelb[] = aResult.channel[2];
    float bChannelb[] = bResult.channel[2];
    
    for(int i = out.numElements - 1; i >=0; i--) {
      outChannelr[i] = aChannelr[i] * aCoeff + bChannelr[i] * bCoeff;
      outChannelg[i] = aChannelg[i] * aCoeff + bChannelg[i] * bCoeff;
      outChannelb[i] = aChannelb[i] * aCoeff + bChannelb[i] * bCoeff;
    }
    
    // Free the intermediate (note bResult == out, so it is not
    // freed).
    aResult.free();
  }

  public boolean hasExternalInfluences()
  {
    return true;
  }
}


