import Expression from './Expression'
import ExpressionType from '../enums/ExpressionType'
import ReturnWayException from "../exception/ReturnWayException"
import JSONObject from "../instances/JSONObject"

export default class Delegate {
  private exp: Expression;
  private source: string;
  private lambdaOutProps: string[] = [];
  private objectNames: JSONObject;

  constructor(exp: Expression, source: string, objectNames?: JSONObject) {
    this.exp = exp;
    this.source = source;
    if (objectNames) {
      this.objectNames = objectNames;
    } else {
      this.objectNames = new JSONObject();
    }
  }

  // 执行程序，执行前，参数必须实例化
  invoke(params?: JSONObject): any {
    if (params == null) {
      params = new JSONObject();
    }
    // 把初始参数给参数表
    this.objectNames = params;
    // 沿根Expression节点遍历，把delegate传递下去
    this.putDelegate(this.exp);
    // 调用exp的执行过程
    try {
      return this.exp.invoke();
    } catch (returnWay) {
      if (returnWay instanceof ReturnWayException) {
        return returnWay.getReturnObject();
      }
      throw returnWay;
    }
  }

  // 沿根节点递归，传递delegate的过程
  private putDelegate(parent: Expression): void {
    for (const child of parent.children) {
      // 有些节点会放空的子节点
      if (child == null) {
        continue;
      }
      // try特殊处理
      if (child.type === ExpressionType.Try) {
        const exps: Expression[] = child.value as Expression[];
        if (exps[0] != null) {
          exps[0].setDelegate(this);
          this.putDelegate(exps[0]);
        }
        if (exps[1] != null) {
          exps[1].setDelegate(this);
          this.putDelegate(exps[1]);
        }
      }
      child.setDelegate(this);
      this.putDelegate(child);
    }
  }

  // 获取表达式
  getExp(): Expression {
    return this.exp;
  }

  // 获取对象值存储
  getObjectNames(): JSONObject {
    return this.objectNames;
  }

  // 获取lambda外层参数名
  getLambdaOutProps(): string[] {
    return this.lambdaOutProps;
  }

  // 存储对象值
  put(key: string, value: any): void {
    this.objectNames.put(key, value);
  }

  // 获取对象值
  get(key: string): any {
    return this.objectNames.get(key);
  }

  // 检查是否包含某个键
  containsKey(key: string): boolean {
    return this.objectNames.has(key);
  }

  // 获取源代码
  getSource(): string {
    return this.source;
  }

  // 使用参数调用
  apply(params?: JSONObject): any {
    if (params === null) {
      params = new JSONObject();
    }
    const map = this.objectNames;
    if (this.lambdaOutProps.length === 0) {
      this.lambdaOutProps.push(...map.keySet());
    }
    const lambdaMap = new JSONObject(map)
    lambdaMap.put("data", params);
    return this.invoke(lambdaMap);
  }
}
