/**

* This is the common logic for both the Node.js and web browser
* implementations of `debug()`.
*/

function setup(env) {

createDebug.debug = createDebug;
createDebug.default = createDebug;
createDebug.coerce = coerce;
createDebug.disable = disable;
createDebug.enable = enable;
createDebug.enabled = enabled;
createDebug.humanize = require('ms');

Object.keys(env).forEach(key => {
        createDebug[key] = env[key];
});

/**
* Active `debug` instances.
*/
createDebug.instances = [];

/**
* The currently active debug mode names, and names to skip.
*/

createDebug.names = [];
createDebug.skips = [];

/**
* Map of special "%n" handling functions, for the debug "format" argument.
*
* Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
*/
createDebug.formatters = {};

/**
* Selects a color for a debug namespace
* @param {String} namespace The namespace string for the for the debug instance to be colored
* @return {Number|String} An ANSI color code for the given namespace
* @api private
*/
function selectColor(namespace) {
        let hash = 0;

        for (let i = 0; i < namespace.length; i++) {
                hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
                hash |= 0; // Convert to 32bit integer
        }

        return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
}
createDebug.selectColor = selectColor;

/**
* Create a debugger with the given `namespace`.
*
* @param {String} namespace
* @return {Function}
* @api public
*/
function createDebug(namespace) {
        let prevTime;

        function debug(...args) {
                // Disabled?
                if (!debug.enabled) {
                        return;
                }

                const self = debug;

                // Set `diff` timestamp
                const curr = Number(new Date());
                const ms = curr - (prevTime || curr);
                self.diff = ms;
                self.prev = prevTime;
                self.curr = curr;
                prevTime = curr;

                args[0] = createDebug.coerce(args[0]);

                if (typeof args[0] !== 'string') {
                        // Anything else let's inspect with %O
                        args.unshift('%O');
                }

                // Apply any `formatters` transformations
                let index = 0;
                args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
                        // If we encounter an escaped % then don't increase the array index
                        if (match === '%%') {
                                return match;
                        }
                        index++;
                        const formatter = createDebug.formatters[format];
                        if (typeof formatter === 'function') {
                                const val = args[index];
                                match = formatter.call(self, val);

                                // Now we need to remove `args[index]` since it's inlined in the `format`
                                args.splice(index, 1);
                                index--;
                        }
                        return match;
                });

                // Apply env-specific formatting (colors, etc.)
                createDebug.formatArgs.call(self, args);

                const logFn = self.log || createDebug.log;
                logFn.apply(self, args);
        }

        debug.namespace = namespace;
        debug.enabled = createDebug.enabled(namespace);
        debug.useColors = createDebug.useColors();
        debug.color = selectColor(namespace);
        debug.destroy = destroy;
        debug.extend = extend;
        // Debug.formatArgs = formatArgs;
        // debug.rawLog = rawLog;

        // env-specific initialization logic for debug instances
        if (typeof createDebug.init === 'function') {
                createDebug.init(debug);
        }

        createDebug.instances.push(debug);

        return debug;
}

function destroy() {
        const index = createDebug.instances.indexOf(this);
        if (index !== -1) {
                createDebug.instances.splice(index, 1);
                return true;
        }
        return false;
}

function extend(namespace, delimiter) {
        const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);
        newDebug.log = this.log;
        return newDebug;
}

/**
* Enables a debug mode by namespaces. This can include modes
* separated by a colon and wildcards.
*
* @param {String} namespaces
* @api public
*/
function enable(namespaces) {
        createDebug.save(namespaces);

        createDebug.names = [];
        createDebug.skips = [];

        let i;
        const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
        const len = split.length;

        for (i = 0; i < len; i++) {
                if (!split[i]) {
                        // ignore empty strings
                        continue;
                }

                namespaces = split[i].replace(/\*/g, '.*?');

                if (namespaces[0] === '-') {
                        createDebug.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
                } else {
                        createDebug.names.push(new RegExp('^' + namespaces + '$'));
                }
        }

        for (i = 0; i < createDebug.instances.length; i++) {
                const instance = createDebug.instances[i];
                instance.enabled = createDebug.enabled(instance.namespace);
        }
}

/**
* Disable debug output.
*
* @return {String} namespaces
* @api public
*/
function disable() {
        const namespaces = [
                ...createDebug.names.map(toNamespace),
                ...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace)
        ].join(',');
        createDebug.enable('');
        return namespaces;
}

/**
* Returns true if the given mode name is enabled, false otherwise.
*
* @param {String} name
* @return {Boolean}
* @api public
*/
function enabled(name) {
        if (name[name.length - 1] === '*') {
                return true;
        }

        let i;
        let len;

        for (i = 0, len = createDebug.skips.length; i < len; i++) {
                if (createDebug.skips[i].test(name)) {
                        return false;
                }
        }

        for (i = 0, len = createDebug.names.length; i < len; i++) {
                if (createDebug.names[i].test(name)) {
                        return true;
                }
        }

        return false;
}

/**
* Convert regexp to namespace
*
* @param {RegExp} regxep
* @return {String} namespace
* @api private
*/
function toNamespace(regexp) {
        return regexp.toString()
                .substring(2, regexp.toString().length - 2)
                .replace(/\.\*\?$/, '*');
}

/**
* Coerce `val`.
*
* @param {Mixed} val
* @return {Mixed}
* @api private
*/
function coerce(val) {
        if (val instanceof Error) {
                return val.stack || val.message;
        }
        return val;
}

createDebug.enable(createDebug.load());

return createDebug;

}

module.exports = setup;