"use strict";

let errors = require('../errors.js');
let Expression = require('./expression.js');
let Types = require('../types/types.js');
let NumberType = require('../types/number.js').Type;

class Apply extends Expression {
  constructor(parsed, env) {
    super(parsed, env);
    let makeExpression = require('./factory.js');
    this.args = this.parsed.args.map((a) => makeExpression(a, this.env));
  }

  typecheck() {
    this.args.forEach((arg) => arg.typecheck());
    if (this.parsed.func == '==') {
      this.type = this.env.getType('Boolean');
      if (this.args.length != 2) {
        throw new errors.Type(`== takes exactly two arguments`);
      }
      if (!Types.haveEquality(this.args[0].type, this.args[1].type)) {
        throw new errors.Type(`Cannot compare (${this.parsed.func}) ` +
          `${this.args[0].type} to ${this.args[1].type}`);
      }
    } else if (['<', '<=', '>', '>='].indexOf(this.parsed.func) !== -1) {
      this.type = this.env.getType('Boolean');
      if (this.args.length != 2) {
        throw new errors.Type(`== takes exactly two arguments`);
      }
      if (!Types.haveOrdering(this.args[0].type, this.args[1].type)) {
        throw new errors.Type(`Cannot compare (${this.parsed.func}) ` +
          `${this.args[0].type} to ${this.args[1].type}`);
      }
    } else if (['+', '-', '*'].indexOf(this.parsed.func) !== -1) {
      this.type = NumberType.singleton;
      if (this.args.length != 2) {
        throw new errors.Type(`== takes exactly two arguments`);
      }
      if (!Types.isNumeric(this.args[0].type) ||
        !Types.isNumeric(this.args[1].type)) {
        throw new errors.Type(`Cannot do arithmetic (${this.parsed.func}) on ` +
          `${this.args[0].type} and ${this.args[1].type}`);
      }
    } else {
      throw new errors.Unimplemented(`The function ${this.parsed.func} ` +
        `is not implemented. Called at ${this.parsed.source}`);
    }
  }

  evaluate() {
    if (this.parsed.func == '==') {
      let lhs = this.args[0].evaluate();
      let rhs = this.args[1].evaluate();
      if (lhs.equals(rhs)) {
        return this.env.getVar('True');
      } else {
        return this.env.getVar('False');
      }
    }
    throw new errors.Unimplemented(`The function ${this.parsed.func} is not implemented`);
  }

  toString(indent) {
    let inner = this.args.map((arg) => arg.toString()).join(', ');
    return `${this.parsed.func}(${inner})`
  }
}

module.exports = Apply;

@@ -1953,76 +1953,200 @@
     
-if (
+let vals = 
 this.
-p
 ar
-sed.func == '==') %7B%0A      let lhs = this.args%5B0%5D.evaluat
+gs.map((arg) =%3E arg.evaluate());%0A    let toBool = (v) =%3E this.env.getVar(v ? 'True' : 'False');%0A    let toNumber = (v) =%3E %7B%0A      let n = NumberType.singleton.makeDefaultValu
 e();
@@ -2155,75 +2155,169 @@
     
+ 
- let rhs = 
+n.assign(v);%0A      return n;%0A    %7D;%0A    if (
 this.
+p
 ar
-gs%5B1%5D.evaluate();%0A      if (lhs.equals(rhs)
+sed.func == '==') %7B%0A      return toBool(vals%5B0%5D.equals(vals%5B1%5D));%0A    %7D else if (this.parsed.func == '%3C'
 ) %7B%0A
-  
     
@@ -2330,34 +2330,46 @@
 rn t
-his.env.getVar('Tr
+oBool(vals%5B0%5D.value %3C vals%5B1%5D.val
 ue
-'
 );%0A
-  
     
@@ -2379,58 +2379,92 @@
 lse 
-%7B%0A        return this.env.getVar('False');%0A      %7D
+if (this.parsed.func == '+') %7B%0A      return toNumber(vals%5B0%5D.value + vals%5B1%5D.value);
 %0A   

Implement remaining functions for the tokenring model to work