use :node;

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

ForInStatement = module.require('../statements/ForInStatement').ForInStatement,
BlockStatement = module.require('../statements/BlockStatement').BlockStatement;

fn ForInExpression(expression, item, index, array, condition)

extends Node {

this.type = 'ForInExpression';

this.expression = expression;
this.expression.parent = this;

this.item = item;
this.item.parent = this;

this.index = index;
if this.index? {
  this.index.parent = this;
}

this.array = array;
this.array.parent = this;

this.condition = condition;
if this.condition? {
  this.condition.parent = this;
}

}

ForInExpression.prototype.codegen = () -> {

if !super.codegen() {
  return;
}

this.defineIdentifier(this.item);

if this.index? {
  this.defineIdentifier(this.index);
}

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

var pushStatement = {
  "type": "ExpressionStatement",
  "codeGenerated": true,
  "expression": {
    "type": "CallExpression",
    "callee": {
      "type": "MemberExpression",
      "computed": false,
      "object": id,
      "property": {
        "type": "Identifier",
        "name": "push"
      }
    },
    "arguments": [this.expression.codegen()]
  }
};

if this.condition? {
  pushStatement = {
    "type": "IfStatement",
    "codeGenerated": true,
    "test": this.condition.codegen(),
    "consequent": {
      "type": "BlockStatement",
      "body": [pushStatement]
    },
    "alternate": null
  };
}

var forInStatement = new ForInStatement(this.item, this.index, this.array, 
  new BlockStatement([pushStatement]));

var fnBlock = new BlockStatement([
  {
    "type": "VariableDeclaration",
    "declarations": [
      {
        "type": "VariableDeclarator",
        "id": id,
        "init": {
          "type": "ArrayExpression",
          "elements": []
        }
      }
    ],
    "kind": "let",
    "codeGenerated": true
  },
  forInStatement,
  {
    "type": "ReturnStatement",
    "codeGenerated": true,
    "argument": id
  }        
]);

forInStatement.parent = fnBlock;

this.type = "CallExpression";
this.callee = {
  "type": "FunctionExpression",
  "id": null,
  "params": [],
  "defaults": [],
  "body": fnBlock.codegen()
};

::Object.defineProperty(this, 'arguments', { 
  value: [], 
  enumerable: true 
});

return this;

};

ForInExpression.prototype.hasCallExpression = () -> true;

ForInExpression.getNextVariableName = () -> {

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

return "forIn" + this.forInIndex++;

};

ForInExpression.resetVariableNames = () -> {

this.forInIndex = 0;

};

exports.ForInExpression = ForInExpression;