module orelang.operator.FoldOperator;
import orelang.expression.ImmediateValue,
       orelang.expression.IExpression,
       orelang.operator.IOperator,
       orelang.Closure,
       orelang.Engine,
       orelang.Value;
import std.algorithm,
       std.array;

class FoldOperator : IOperator {
  public Value call(Engine engine, Value[] args) {
    Value func  = args[0];
    Value tmp   = args[1];
    Value eargs = engine.eval(args[2]);

  //if (eargs.convertsTo!(Value[])) {
  if (eargs.type == ValueType.Array) {
    Value[] array = eargs.getArray;
    Value efunc = engine.eval(func);

    if (efunc.type == ValueType.Closure) {
      foreach (elem; array) {
        tmp = efunc.getClosure.eval([tmp, elem]);
      }
    } else if (efunc.type == ValueType.IOperator) {
      foreach (elem; array) {
        tmp = efunc.getIOperator.call(engine, [tmp, elem]);
      }
    }

    return tmp;
  } else {
      if (!(eargs.type == ValueType.ImmediateValue) && !(eargs.getImmediateValue.value.type == ValueType.Array)) {
        throw new Error("Fold requires array and function as a Operator");
      }

      Value[] array = eargs.getImmediateValue.value.getArray;
      Value efunc = engine.eval(func);

      if (efunc.type == ValueType.Closure) {
        foreach (elem; array) {
          tmp = efunc.getClosure.eval([tmp, elem]);
        }
      } else {
              foreach (elem; array) {
        tmp = efunc.getIOperator.call(engine, [tmp, elem]);
      }
      }

      return tmp;
    }
  }
}