use :node;

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

fn SwitchStatement(discriminant, cases)

extends Node {

this.type = 'SwitchStatement';

this.discriminant = discriminant;
this.discriminant.parent = this;

this.cases = cases;

for caseClause in this.cases {
  caseClause.parent = this;
}

}

SwitchStatement.prototype.codegen = () -> {

if !super.codegen() {
  return;
}

var context = this.getContext();
var firstCase, currentCase, defaultCase;
var fallthroughPosition = 1;

this.discriminant = this.discriminant.codegen();

if this.discriminant.hasCallExpression() {
  var id = {
    "type": "Identifier",
    "name": SwitchStatement.getNextVariableName()
  };

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

  this.discriminant = id;
}

var hasFallthrough = false;

for caseClause in this.cases {
  if (!caseClause.tests) {
    defaultCase = caseClause;
    break;
  }

  if !firstCase? {
    firstCase = caseClause.codegen();
    currentCase = firstCase;
  } else {
    if currentCase.fallthrough {
      hasFallthrough = true;
      currentCase = caseClause.codegen(this.branchFallthrough);
      context.node.body.splice(context.position + fallthroughPosition++, 0, currentCase);
    } else {        
      currentCase.alternate = caseClause.codegen(this.fallthroughId?);
      currentCase = currentCase.alternate;
    }      
  }
}

if hasFallthrough {
  for caseClause in this.cases {
    if !caseClause.fallthrough and caseClause != defaultCase {
      caseClause.body.body = [{
        "type": "ExpressionStatement",
        "codeGenerated": true,
        "expression": {
          "type": "AssignmentExpression",
          "operator": "=",
          "left": this.fallthroughId,
          "right": {
            "type": "Literal",
            "value": 2
          }
        }
      }].concat(caseClause.body.body);
    }
  }
}

if defaultCase? {
  if !firstCase? {
    Node.getErrorManager().error({
      type: "SingleDefaultClause",
      message: "default clause without other case clauses is disallowed.",
      loc: defaultCase.loc
    });
  } else {
    if currentCase.fallthrough {
      defaultCase = defaultCase.codegen(this.fallthroughId?);
      defaultCase.codeGenerated = true;
      if this.fallthroughId? {
        context.node.body.splice(context.position + fallthroughPosition++, 0, defaultCase);
      } else {
        for statement in defaultCase.body {
          context.node.body.splice(context.position + fallthroughPosition++, 0, statement);
        }
      }        
    } else {
      currentCase.alternate = defaultCase.codegen(this.fallthroughId?);
    }
  }
}

if this.fallthroughId? {
  context.node.body.splice(context.position, 0, {
    "type": "VariableDeclaration",
    "codeGenerated": true,
    "declarations": [
      {
        "type": "VariableDeclarator",
        "id": this.fallthroughId,
        "init": {
          "type": "Literal",
          "value": 0
        }
      }
    ],
    "kind": "let"
  });
}

if !firstCase? {
  this.type = "ExpressionStatement";
  this.expression = this.discriminant;
} else {
  this.type = firstCase.type;
  this.test = firstCase.test;
  this.consequent = firstCase.consequent;
  this.alternate = firstCase.alternate;
}

return this;

};

SwitchStatement.getNextVariableName = () -> {

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

return "switchStatement" + this.switchStatementIndex++;

};

SwitchStatement.resetVariableNames = () -> {

this.switchStatementIndex = 0;

};

exports.SwitchStatement = SwitchStatement;