/**
 * @fileoverview Rule to flag when the same variable is declared more then once.
 * @author Ilya Volodin
 */

"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {
    var options = {
        builtinGlobals: Boolean(context.options[0] && context.options[0].builtinGlobals)
    };

    /**
     * Find variables in a given scope and flag redeclared ones.
     * @param {Scope} scope - An escope scope object.
     * @returns {void}
     * @private
     */
    function findVariablesInScope(scope) {
        scope.variables.forEach(function(variable) {
            var hasBuiltin = options.builtinGlobals && "writeable" in variable;
            var count = (hasBuiltin ? 1 : 0) + variable.identifiers.length;

            if (count >= 2) {
                variable.identifiers.sort(function(a, b) {
                    return a.range[1] - b.range[1];
                });

                for (var i = (hasBuiltin ? 0 : 1), l = variable.identifiers.length; i < l; i++) {
                    context.report(
                        variable.identifiers[i],
                        "'{{a}}' is already defined",
                        {a: variable.name});
                }
            }
        });

    }

    /**
     * Find variables in the current scope.
     * @param {ASTNode} node - The Program node.
     * @returns {void}
     * @private
     */
    function checkForGlobal(node) {
        var scope = context.getScope(),
            parserOptions = context.parserOptions,
            ecmaFeatures = parserOptions.ecmaFeatures || {};

        // Nodejs env or modules has a special scope.
        if (ecmaFeatures.globalReturn || node.sourceType === "module") {
            findVariablesInScope(scope.childScopes[0]);
        } else {
            findVariablesInScope(scope);
        }
    }

    /**
     * Find variables in the current scope.
     * @returns {void}
     * @private
     */
    function checkForBlock() {
        findVariablesInScope(context.getScope());
    }

    if (context.parserOptions.ecmaVersion >= 6) {
        return {
            "Program": checkForGlobal,
            "BlockStatement": checkForBlock,
            "SwitchStatement": checkForBlock
        };
    } else {
        return {
            "Program": checkForGlobal,
            "FunctionDeclaration": checkForBlock,
            "FunctionExpression": checkForBlock,
            "ArrowFunctionExpression": checkForBlock
        };
    }
};

module.exports.schema = [
    {
        "type": "object",
        "properties": {
            "builtinGlobals": {"type": "boolean"}
        },
        "additionalProperties": false
    }
];
