/// Evolver Copyright 2000, Morgan McGuire
/// matrix@alum.mit.edu   http://graphics3d.com/matrix
/**
 * An operator expression.
 */
public class ELCall extends AbstractELExpression implements ELExpression
{
  ELOperator operator;
  ELExpression child[];
  int complexityScore = 1;
  boolean _hasExternalInfluences;
  
  /**
   * @param subExpressions array is used directly, not cloned.
   */
  protected ELCall(ELOperator operator, ELExpression subExpressions[])
  {
    this.operator = operator;
    child = subExpressions;

    _hasExternalInfluences = operator.hasExternalInfluences();

    complexityScore = 1;
    for(int c = child.length - 1; c >= 0; c--) {
      complexityScore += child[c].numNestedSubexpressions();
      _hasExternalInfluences = _hasExternalInfluences || child[c].hasExternalInfluences();
    }
  }

  public boolean hasExternalInfluences()
  {
    return _hasExternalInfluences;
  }

  public ELExpression geneticClone(ELCall parent, GeneticParameters gp)
  {
    if (gp.mutationOccurs(parent)) {
      // Select mutation type.
      return randomMutation(parent, gp);
    } else {
      return cloneWithChildMutations(gp);
    }
  }

  public ELExpression cloneWithReplacement(ELExpression e, int p) 
  {
    if (p == 0) {
      return e;
    }
   
    ELExpression newChild[] = new ELExpression[child.length];
    int childsNumber = 1;
    
    for(int c = 0; c < child.length; c++) {
      ELExpression thisChild = child[c];
      int childNumSubexpressions = thisChild.numNestedSubexpressions();
      int childRangeUpperBound = childNumSubexpressions + childsNumber;

      newChild[c] = thisChild.cloneWithReplacement(e, p - childsNumber);

      childsNumber = childRangeUpperBound;
    }
    
    return new ELCall(operator, newChild);
  }

  /**
   * Returns the number of sub expressions (formal parameters) in this
   * call.
   */
  public int numSubexpressions()
  {
    return child.length;
  }

  private ELExpression randomMutation(ELCall parent, GeneticParameters gp)
  {
    while (true) {

      int mutation = gp.randomMutationType();
      switch (mutation) {
      case GeneticParameters.CHANGE_OPERATOR_MUTATION:
        return changeOperatorMutation(gp);
        
      case GeneticParameters.ENCLOSE_IN_NEW_CALL_MUTATION:
        return encloseInNewCallMutation(gp);
        
      case GeneticParameters.NEW_EXPRESSION_MUTATION:
        return gp.randomExpression();
        
      case GeneticParameters.SIMPLIFY_MUTATION:
          if (child.length > 0) {
            return simplifyMutation(parent, gp);
          } else {
            break;
          }
        
      case GeneticParameters.CLONE_SIBLING_MUTATION:
        if (parent != null) {
          return cloneSiblingMutation(parent, gp);
        } else {
          break;
        }
      default:
        // not an appropriate mutation; keep cycling.
      }
    }
  }

  /**
   * Genetically clones children, possibly introducing mutations.
   */  
  private ELExpression[] geneticCloneChildren(GeneticParameters gp)
  {
    // Clone sub expressions
    ELExpression subExpr[] = new ELExpression[child.length];

    for(int c = child.length - 1; c >= 0; c--) {
      subExpr[c] = child[c].geneticClone(this, gp);
    }
    return subExpr;
  }

  private ELExpression changeOperatorMutation(GeneticParameters gp)
  {
    ELOperator operator = gp.randomOperator();
    
    // Adjust the argument list for the new operator set.
    int min = operator.getMinNumArguments();
    int max = operator.getMaxNumArguments();
    
    ELExpression args[] = geneticCloneChildren(gp);

    if (min > child.length) {
      // need more arguments
      ELExpression newArgs[] = new ELExpression[min];
      System.arraycopy(args, 0, newArgs, 0, args.length);

      for(int a = child.length; a < min; a++) {
        newArgs[a] = gp.randomExpression();
      }
      args = newArgs;

    } else if (max < child.length) {

      // need fewer arguments
      ELExpression newArgs[] = new ELExpression[max];
      System.arraycopy(args, 0, newArgs, 0, max);
      args = newArgs;
    } 

    return new ELCall(operator, args);
  }

  protected ELExpression cloneWithChildMutations(GeneticParameters gp)
  {
    return new ELCall(operator, geneticCloneChildren(gp));
  }

  /**
   * Returns the genetic clone of one child, chosen at random.  
   */
  private ELExpression simplifyMutation(ELCall parent, GeneticParameters gp)
  {
    if (child.length == 0) {
      throw new Error("Child length is zero");
    }

    return randomChild().geneticClone(this, gp);
  }
  

  /**
   * Randomly chooses a child expression and returns it.  Don't mutate
   * the result.
   */
  ELExpression randomChild()
  {
    if (child.length == 0) {
      throw new Error("Called randomChild() on an expression with no children!");
    }

    return child[(int)Math.floor(Math.random() * child.length)];
  }

  public String unparse()
  {
    String s = operator.unparse() + "(";
    String sep = "";

    for(int c = 0; c < child.length; c++) {
      s = s + sep + child[c].unparse();
      sep = ", ";
    }
    
    s = s + ")";

    return s;
  }

  public String toString()
  {
    return operator.unparse() + "(...)";
  }

  public int numNestedSubexpressions()
  {
    return complexityScore;
  }  

  public ELExpression randomSubexpression()
  {
    int s = (int)Math.floor(Math.random() * numNestedSubexpressions());
    return getNestedSubexpression(s);
  }

  /**
   * Returns a nested subexpression.  Subexpressions are numbered from
   * the root in a depth first manner.
   *
   * @param targetNumber The number of the node we're looking for.  The root is zero.
   */
  public ELExpression getNestedSubexpression(int targetNumber) 
  {
    // Basic algorithm: Assign a number to each subexpression by depth first
    // search from the root:
    //
    //                  0
    //                 / \
    //                1   4  
    //               / \
    //              2   3
    //
    // Tell each sub-tree what its base number is

    if (0 == targetNumber) {
      // Base case; this is the node we're looking for
      return this;
    }

    if (targetNumber < 0) {
      // something has gone wrong; we already passed the node we're looking for
      throw new Error("Bad search... already passed node " + targetNumber);
    }
    
    // look at each of the children
    int childsNumber = 1;
    
    for(int c = 0; c < child.length; c++) {
      ELExpression thisChild = child[c];
      int childNumSubexpressions = thisChild.numNestedSubexpressions();
      int childRangeUpperBound = childNumSubexpressions + childsNumber;

      if (childRangeUpperBound > targetNumber) {
        // it is in the child
        return thisChild.getNestedSubexpression(targetNumber - childsNumber);
      }

      childsNumber = childRangeUpperBound;
    }

    // Something went wrong; we couldn't find the node
    throw new Error("Bad search... couldn't find node " + targetNumber);
  }

  public void evaluate(ELMatrix out, ELEnvironment environment, ParameterList pl)
  {
    final ELMatrix argument[] = new ELMatrix[child.length];

    int width = out.getWidth();
    int height = out.getHeight();
    int numChildren = child.length;

    if (environment.multiThreadEvaluation) {
      // Multi-thread
      Thread thread[] = new Thread[child.length];
      
      for(int a = numChildren - 1; a >= 0; a--) {
        argument[a] = ELMatrix.alloc(width, height);
        thread[a] = new EvaluationThread(child[a], argument[a], environment, pl);
        thread[a].start();
      }

      // Wait until all threads complete
      int numThreadsRunning = 0;
      do {
        Thread.yield();
        numThreadsRunning = 0;
        for(int a = numChildren - 1; a >= 0; a--) {
          if (thread[a].isAlive()) {
            numThreadsRunning += 1;
          }
        }
      } while (numThreadsRunning > 0);
      
    } else {
      // Single thread
      for(int a = numChildren - 1; a >= 0; a--) {
        argument[a] = ELMatrix.alloc(width, height);
        child[a].evaluate(argument[a], environment, pl);
      }
    }
    operator.apply(argument, out, environment);

    // Deallocate matrices returned by children.
    for(int a = argument.length - 1; a >= 0; a--) {
      argument[a] = (ELMatrix)argument[a].free();
    }    
  }

  public Object clone()
  {
    ELExpression newChild[] = new ELExpression[child.length];

    for(int c = child.length - 1; c >= 0; c--) {
      newChild[c] = (ELExpression)child[c].clone();
    }

    return new ELCall(operator, newChild);
  }


  /**
   * Returns the maximum depth of this expression, where depth is the
   * maximum number of expressions enclosing a subexpression.  A
   * scalar, vector, or procedure with no subexpressions has depth 0.
   */
  public int depth()
  {
    if (child.length == 0) {
      return 0;
    }

    int childDepth = 0;

    // Find deepest child
    for(int i=0; i < child.length; i++) {
      if (child[i] instanceof ELCall) {
        ELCall call = (ELCall)child[i];
        int d = call.depth();

        if (d > childDepth) {
          childDepth = d;
        }
      }
    }

    return childDepth + 1;
  }
}


/**
 * A thread that evaluates a single expression and stores the result in out.
 */
class EvaluationThread extends Thread
{
    ELExpression expression;
    ELMatrix out; 
    ELEnvironment environment;
    ParameterList pl;
    
    public EvaluationThread(ELExpression expression, ELMatrix out, ELEnvironment environment, ParameterList pl) 
    {
	this.expression = expression;
	this.out = out;
	this.environment = environment;
    }
    
    public void run() 
    {
	expression.evaluate(out, environment, pl);
    }
}
