/**
 * @fileoverview Rule to forbid control charactes from regular expressions.
 * @author Nicholas C. Zakas
 */

"use strict";

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

module.exports = {
    meta: {
        docs: {
            description: "disallow control characters in regular expressions",
            category: "Possible Errors",
            recommended: true
        },

        schema: []
    },

    create: function(context) {

        /**
         * Get the regex expression
         * @param {ASTNode} node node to evaluate
         * @returns {*} Regex if found else null
         * @private
         */
        function getRegExp(node) {
            if (node.value instanceof RegExp) {
                return node.value;
            } else if (typeof node.value === "string") {

                var parent = context.getAncestors().pop();

                if ((parent.type === "NewExpression" || parent.type === "CallExpression") &&
                    parent.callee.type === "Identifier" && parent.callee.name === "RegExp"
                ) {

                    // there could be an invalid regular expression string
                    try {
                        return new RegExp(node.value);
                    } catch (ex) {
                        return null;
                    }
                }
            }

            return null;
        }

        /**
         * Check if given regex string has control characters in it
         * @param {String} regexStr regex as string to check
         * @returns {Boolean} returns true if finds control characters on given string
         * @private
         */
        function hasControlCharacters(regexStr) {

            // check control characters, if RegExp object used
            var hasControlChars = /[\x00-\x1f]/.test(regexStr); // eslint-disable-line no-control-regex

            // check substr, if regex literal used
            var subStrIndex = regexStr.search(/\\x[01][0-9a-f]/i);

            if (!hasControlChars && subStrIndex > -1) {

                // is it escaped, check backslash count
                var possibleEscapeCharacters = regexStr.substr(0, subStrIndex).match(/\\+$/gi);

                hasControlChars = possibleEscapeCharacters === null || !(possibleEscapeCharacters[0].length % 2);
            }

            return hasControlChars;
        }

        return {
            Literal: function(node) {
                var computedValue,
                    regex = getRegExp(node);

                if (regex) {
                    computedValue = regex.toString();

                    if (hasControlCharacters(computedValue)) {
                        context.report(node, "Unexpected control character in regular expression.");
                    }
                }
            }
        };

    }
};
