var acorn = require("acorn");
var debug = require('debug')('JsValidator');

var JsValidator = function(errorHandler) {
  if (!(this instanceof JsValidator)) {
    return new JsValidator(errorHandler);
  }

  this.errorHandelr = errorHandler;

  this.JsSpec = require('../data/js_spec.json');
}

JsValidator.prototype.validate = function(source, filePath, startLine) {
  this.startLine = startLine;
  this.filePath = filePath;

  this.ast = acorn.parse(source);
  this._check(this.ast);
}

JsValidator.prototype._printError = function(errorName, ref) {
  return this.errorHandelr.printError(errorName, this.filePath, this.startLine, ref);
};

JsValidator.prototype._check = function(node) {
  var self = this;
  if (!node) return;

  var self = this;

  if (Array.isArray(node)) {
    return node.map(function(o) {
      self._check(o);
    });
  }

  switch (node.type) {
    case "Program":
      self._check(node.body);
      return;
    case "WithStatement":
      self._printError('JS_STATEMENT_WITH', node.object);
      return;
    case "ExpressionStatement":
      self._check(node.expression);
      return;
    case "AssignmentExpression":
      self._check(node.left);
      self._check(node.right);
      return;
    case "MemberExpression":
      if (node.object.type === 'Identifier' && node.object.name === 'document') {
        if ( node.property.type === 'Identifier' 
          && self.JsSpec['document'].indexOf(node.property.name) !== -1) {
          self._printError('JS_INVALID_CALL', node.object);
        }
        // console.log(node.object);
        // console.log(node.property);       
      }

      self._check(node.object);
      self._check(node.property);
      return;
    case "VariableDeclaration":
      self._check(node.declarations);
      return;
    case "VariableDeclarator":
      self._check(node.id);
      self._check(node.init);
      return;
    case "CallExpression":
    case "NewExpression":
      self._check(node.callee);
      self._check(node.arguments);
      return;
    case "ReturnStatement":
      self._check(node.argument);
      return;
    case "FunctionExpression":
      self._check(node.params);
      self._check(node.body);
      return;
    case "FunctionDeclaration":
      self._check(node.id);
      self._check(node.params);
      self._check(node.body);
      return;
    case "SequenceExpression":
      self._check(node.expressions);
      return;
    case "LogicalExpression":
      self._check(node.left);
      self._check(node.right);
      return;
    case "BlockStatement":
      self._check(node.body);
      return;
    case "BinaryExpression":
      self._check(node.left);
      self._check(node.right);
      return;
    case "UnaryExpression":
      self._check(node.argument);
      return;
    case "IfStatement":
    case "ConditionalExpression":
      self._check(node.test);
      self._check(node.consequent);
      self._check(node.alternate);
      return;
    case "SwitchStatement":
      self._check(node.discriminant);
      self._check(node.cases);
      return;
    case "SwitchCase":
      self._check(node.consequent);
      self._check(node.test);
      return;
    case "ForStatement":
      self._check(node.test);
      self._check(node.update);
      self._check(node.body);
      return;
    case "ForInStatement":
      self._check(node.left);
      self._check(node.right);
      self._check(node.body);
      return;
    case "WhileStatement":
    case "DoWhileStatement":
      self._check(node.test);
      self._check(node.body);
      return;
    case "TryStatement":
      self._check(node.block);
      self._check(node.handler);
      return;
    case "CatchClause":
      self._check(node.param);
      self._check(node.body);
      return;
    case "ThrowStatement":
      self._check(node.argument);
      return;
    case "ArrayExpression":
      self._check(node.elements);
      return;
    case "ObjectExpression":
      self._check(node.properties);
      return;
    case "Property":
      self._check(node.key);
      self._check(node.value);
      return;
    case "UpdateExpression":
      self._check(node.argument);
      return;
    case "Identifier":
    case "Literal":
    case "ThisExpression":
    case "ContinueStatement":
    case "BreakStatement":
    case "EmptyStatement":
      return;
  }

  // Unkown type
  console.log(node);
  throw new TypeError('Unkown JavaScript Type.');
}

module.exports = JsValidator
