"use strict";
var esprima = require('esprima');
var estraverse = require('estraverse');
var _ = require('underscore');
var utils = require('../utils');
function updateFunctionStatement(oldCoverageObj, newCoverageObj) {
    var oldStatements = oldCoverageObj.s;
    var newStatements = newCoverageObj.s;
    var retValue = {
        'update': false,
        'statementKey': undefined
    };
    for (var stmtkey in oldStatements) {
        if (oldStatements.hasOwnProperty(stmtkey) && newStatements.hasOwnProperty(stmtkey)) {
            if (oldStatements[stmtkey] !== newStatements[stmtkey]) {
                retValue.update = true;
                retValue.statementKey = stmtkey;
                break;
            }
        }
    }
    return retValue;
}
exports.updateFunctionStatement = updateFunctionStatement;
function updateFunctionBranch(oldCoverageObj, newCoverageObj) {
    var oldBranches = oldCoverageObj.b;
    var newBranches = newCoverageObj.b;
    var oldResultCondition;
    var newResultCondition;
    var substractionCondition;
    var retValue = {
        'update': false,
        'branchesKeys': [],
        'conditionsResults': []
    };
    for (var branchKey in oldBranches) {
        if (oldBranches.hasOwnProperty(branchKey) && newBranches.hasOwnProperty(branchKey)) {
            oldResultCondition = _.clone(oldBranches[branchKey]);
            newResultCondition = _.clone(newBranches[branchKey]);
            if (_.isArray(oldResultCondition) && _.isArray(newResultCondition)) {
                if (oldResultCondition.length === newResultCondition.length) {
                    if (!_.isEqual(oldResultCondition, newResultCondition)) {
                        try {
                            substractionCondition = arraysSubstraction(newResultCondition, oldResultCondition);
                            if (!retValue.update) {
                                retValue.update = true;
                            }
                            retValue.branchesKeys.push(branchKey);
                            retValue.conditionsResults.push(_.clone(substractionCondition));
                        }
                        catch (e) {
                            throw e;
                        }
                    }
                }
            }
        }
    }
    return retValue;
}
exports.updateFunctionBranch = updateFunctionBranch;
function arraysSubstraction(a, b) {
    if (a.length !== b.length) {
        throw new Error('[arraysSubstraction] Different lengths.');
    }
    var res = [];
    var valueSub;
    for (var k = 0; k < a.length; k++) {
        valueSub = a[k] - b[k];
        if (valueSub !== 0 && valueSub !== 1) {
            throw new Error('[arraysSubstraction] Exception. Reason: each element must be 0 or 1 ' +
                'instead of ' + valueSub + ' ([' + a.join(', ') + ']' +
                ' - [' + b.join(', ') + '])');
        }
        res.push(valueSub);
    }
    return res;
}
function getLocationOfInstrumentedFunction(functionInstance) {
    var functionName = functionInstance.name;
    var pathFileFunction = functionInstance.pathFile;
    var contentFile = utils.readFile(pathFileFunction);
    var astFile;
    var location = null;
    var errorPrefix = '[getLocationInstrumentedFunction] Exception. Reason: ';
    if (contentFile === null) {
        throw new Error(errorPrefix + 'content of file is null');
    }
    try {
        astFile = esprima.parse(contentFile, { loc: true });
    }
    catch (e) {
        throw new Error(errorPrefix + 'Unable to parse the AST of the function');
    }
    estraverse.traverse(astFile, {
        enter: function (node) {
            if (node.type === 'FunctionDeclaration') {
                if (node.id.type === 'Identifier' && node.id.name === functionName) {
                    if (node.body.type === 'BlockStatement' && _.isArray(node.body.body) &&
                        node.body.body.length > 0) {
                        location = {};
                        location.line = node.body.body[0].loc.start.line;
                        location.column = node.body.body[0].loc.start.column;
                        this.break();
                    }
                    else {
                        throw new Error(errorPrefix + 'Unable to get the location of the function');
                    }
                }
            }
        }
    });
    return location;
}
exports.getLocationOfInstrumentedFunction = getLocationOfInstrumentedFunction;
function resolveInjectedVariables(chrome, currentScope, injectedVariables, isMainInjVariable, currentObject, cb) {
    if (injectedVariables.length === 0) {
        cb(null);
    }
    else {
        resolveInjectedVariable(chrome, currentScope, 0, injectedVariables, isMainInjVariable, currentObject, function (err) {
            if (err) {
                cb(err);
            }
            else {
                cb(null);
            }
        });
    }
}
exports.resolveInjectedVariables = resolveInjectedVariables;
function resolveInjectedVariable(chrome, currentScope, index, injectedVariables, isMainInjVariable, currentObject, cb) {
    if (index < injectedVariables.length) {
        var injVarName = injectedVariables[index].name;
        var injVarObjectId = injectedVariables[index].objectId;
        currentObject[injVarName] = {};
        chrome.send('Runtime.getProperties', { 'objectId': injVarObjectId }, function (err, res) {
            if (err) {
                cb(new Error('Unable to get objeect var ' + injectedVariables[index].name));
            }
            else {
                var varName;
                var varValue;
                var varType;
                var newInjectedVariables = [];
                for (var k = 0; k < res.result.length; k++) {
                    try {
                        varName = res.result[k].name;
                        varValue = res.result[k].value.value;
                        varType = res.result[k].value.type;
                        if (varType === 'object') {
                            newInjectedVariables.push({
                                'name': varName,
                                'objectId': res.result[k].value.objectId
                            });
                        }
                        else {
                            currentObject[injVarName][varName] = varValue;
                        }
                    }
                    catch (e) {
                    }
                }
                resolveInjectedVariables(chrome, currentScope, newInjectedVariables, false, currentObject[injVarName], function (err) {
                    if (err) {
                        cb(new Error('second error Runtime.getProperties ' +
                            injectedVariables[index].name));
                    }
                    else {
                        if (isMainInjVariable) {
                            currentScope[injVarName] = currentObject[injVarName];
                        }
                        if (index === (injectedVariables.length - 1)) {
                            cb(null);
                        }
                        else {
                            resolveInjectedVariable(chrome, currentScope, ++index, injectedVariables, isMainInjVariable, currentObject, cb);
                        }
                    }
                });
            }
        });
    }
}
