package edu.washington.escience.myria.expression.evaluate;

import java.lang.reflect.InvocationTargetException;

import org.codehaus.commons.compiler.CompileException;
import org.codehaus.janino.ExpressionEvaluator;

import com.google.common.base.Preconditions;

import edu.washington.escience.myria.DbException;
import edu.washington.escience.myria.ReadableTable;
import edu.washington.escience.myria.TupleBatch;
import edu.washington.escience.myria.Type;
import edu.washington.escience.myria.column.Column;
import edu.washington.escience.myria.column.ConstantValueColumn;
import edu.washington.escience.myria.column.builder.WritableColumn;
import edu.washington.escience.myria.expression.Expression;
import edu.washington.escience.myria.expression.StateExpression;
import edu.washington.escience.myria.expression.VariableExpression;
import edu.washington.escience.myria.operator.StatefulApply;

/**
 * An Expression evaluator for generic expressions that produces a constant such as the initial state in
 * {@link StatefulApply}.
 */
public final class ConstantEvaluator extends GenericEvaluator {
  /** An empty object array passed to Janino as no arguments. */
  private static final Object[] NO_ARGS = new Object[] {};
  /** The value of this expression. */
  private final Object value;
  /** The type of the value of this expression. */
  private final Type type;

  /**
   * Default constructor.
   * 
   * @param expression the expression for the evaluator
   * @param parameters parameters that are passed to the expression
   * @throws DbException if there is an error compiling the expression
   */
  public ConstantEvaluator(final Expression expression, final ExpressionOperatorParameter parameters)
      throws DbException {
    super(expression, parameters);
    Preconditions.checkArgument(!expression.hasOperator(VariableExpression.class)
        && !expression.hasOperator(StateExpression.class), "Expression %s does not evaluate to a constant", expression);
    type = expression.getOutputType(parameters);
    try {
      evaluator =
          new ExpressionEvaluator(expression.getJavaExpression(parameters), type.toJavaType(), new String[] {},
              new Class<?>[] {});
      value = evaluator.evaluate(NO_ARGS);
    } catch (CompileException e) {
      throw new DbException("Error when compiling expression " + this, e);
    } catch (InvocationTargetException e) {
      throw new DbException("Error when evaluating expression " + this, e);
    }
  }

  /**
   * Expression evaluator.
   */
  private ExpressionEvaluator evaluator;

  /**
   * Creates an {@link ExpressionEvaluator} from the {@link #javaExpression}. This does not really compile the
   * expression and is thus faster.
   */
  @Override
  public void compile() {
    /* Do nothing! */
  }

  /**
   * Evaluates the {@link #getJavaExpression()} using the {@link #evaluator}.
   * 
   * @return the result from the evaluation
   */
  public Object eval() {
    return value;
  }

  @Override
  public void eval(final TupleBatch tb, final int rowIdx, final WritableColumn result, final ReadableTable state) {
    throw new UnsupportedOperationException("Should not be here. Should be using eval() instead");
  }

  @Override
  public Column<?> evaluateColumn(final TupleBatch tb) {
    return new ConstantValueColumn((Comparable<?>) value, type, tb.numTuples());
  }
}

@@ -2028,106 +2028,257 @@
     
-try %7B%0A      evaluator =%0A          new ExpressionEvaluator(expression.getJavaExpression(parameters)
+String java;%0A    try %7B%0A      java = expression.getJavaExpression(parameters);%0A    %7D catch (Exception e) %7B%0A      throw new DbException(%22Error when generating Java expression %22 + this, e);%0A    %7D%0A    try %7B%0A      evaluator = new ExpressionEvaluator(java
 , ty
@@ -2314,22 +2314,8 @@
  %7B%7D,
-%0A             
  new
@@ -2466,36 +2466,36 @@
  expression %22 + 
-this
+java
 , e);%0A    %7D catc
@@ -2586,36 +2586,36 @@
  expression %22 + 
-this
+java
 , e);%0A    %7D%0A  %7D%0A
@@ -2664,16 +2664,22 @@
 private 
+final 
 Expressi

better error message