use :node;

var Node = module.require('../Node').Node;

fn ExistentialExpression(argument)

extends Node {

this.type = 'ExistentialExpression';
this.argument = argument;
this.argument.parent = this;

}

ExistentialExpression.prototype.codegen = () -> {

if !super.codegen() {
  return;
}

var isArgumentCallExpression = this.argument.hasCallExpression?() ?? false;

this.argument = this.argument.codegen();

// If the argument has a function call (e.g: a().b)
// then store its value in a separate variable to avoid
// calling the function twice.
if isArgumentCallExpression {
  var context = this.getContext();

  var id = {
    "type": "Identifier",
    "name": ExistentialExpression.getNextVariableName()    
  };

  context.node.body.splice(context.position + 
    (ExistentialExpression.existentialIndex - 2), 0, {
    "type": "VariableDeclaration",
    "declarations": [
      {
        "type": "VariableDeclarator",
        "id": id,
        "init": this.argument
      }
    ],
    "kind": "let",
    "codeGenerated": true
  });

  this.argument = id;
}

// The generated syntax looks like:
// argument === null
var nullCheck = {
  "type": "BinaryExpression",
  "operator": "!==",
  "left": this.argument,
  "right": {
    "type": "Literal",
    "value": null,
    "raw": "null"
  }
};

// Add undefined check
this.type = 'LogicalExpression';
this.operator = '&&';
this.left = {
  "type": "BinaryExpression",
  "operator": "!==",
  "left": {
    "type": "UnaryExpression",
    "operator": "typeof",
    "argument": this.argument,
    "prefix": true
  },
  "right": {
    "type": "Literal",
    "value": "undefined",
    "raw": "'undefined'"
  }
};

this.right = nullCheck;
return this;

};

ExistentialExpression.prototype.hasCallExpression = () -> {

return this.argument?.hasCallExpression?() ?? false;

};

ExistentialExpression.getNextVariableName = () -> {

if !this.existentialIndex? { 
  this.existentialIndex = 0;
}

return "existential" + this.existentialIndex++;

};

ExistentialExpression.resetVariableNames = () -> {

this.existentialIndex = 0;

};

exports.ExistentialExpression = ExistentialExpression;