"use strict";
var path_1 = require('path');
var fs_1 = require('fs');
var task_runner_1 = require("./task.runner");
var _ = require('../lodash.custom');
var debug = require('debug')('cb:task-utils');
(function (InputErrorTypes) {
    InputErrorTypes[InputErrorTypes["InputFileNotFound"] = "InputFileNotFound"] = "InputFileNotFound";
    InputErrorTypes[InputErrorTypes["NoTasksAvailable"] = "NoTasksAvailable"] = "NoTasksAvailable";
    InputErrorTypes[InputErrorTypes["NoWatchersAvailable"] = "NoWatchersAvailable"] = "NoWatchersAvailable";
    InputErrorTypes[InputErrorTypes["FileNotFound"] = "FileNotFound"] = "FileNotFound";
    InputErrorTypes[InputErrorTypes["NotAFile"] = "NotAFile"] = "NotAFile";
    InputErrorTypes[InputErrorTypes["InvalidYaml"] = "InvalidYaml"] = "InvalidYaml";
    InputErrorTypes[InputErrorTypes["InvalidInput"] = "InvalidInput"] = "InvalidInput";
    InputErrorTypes[InputErrorTypes["InvalidJson"] = "InvalidJson"] = "InvalidJson";
})(exports.InputErrorTypes || (exports.InputErrorTypes = {}));
var InputErrorTypes = exports.InputErrorTypes;
function locateModule(config, taskName) {
    var tasksByName = locateExternalTask(config, taskName);
    /**
     * Exit early if this file exists
     * TODO - allow this lookup to be cached to prevent future file IO
     * TODO - skip file/node look-ups when key matches top-level task definition
     */
    if (tasksByName.length)
        return tasksByName;
    var tasksByRequire = locateNodeModule(config, taskName);
    if (tasksByRequire.length)
        return tasksByRequire;
    return [];
}
exports.locateModule = locateModule;
function locateExternalTask(config, name) {
    var dirLookups = config.tasksDir.reduce(function (acc, dir) {
        return acc.concat([[dir, name + '.js'], [dir, name]]);
    }, []);
    var lookups = dirLookups.concat([
        [name + '.js'],
        [name]
    ]);
    return lookups
        .map(function (x) { return path_1.resolve.apply(null, [config.cwd].concat(x)); })
        .filter(fs_1.existsSync)
        .filter(function (x) { return fs_1.lstatSync(x).isFile(); })
        .map(function (resolvedFilePath) {
        return {
            rawInput: name,
            parsed: path_1.parse(resolvedFilePath),
            resolved: resolvedFilePath,
            relative: path_1.relative(config.cwd, resolvedFilePath),
            errors: []
        };
    });
}
function locateNodeModule(config, name) {
    try {
        var maybe = path_1.join.apply(void 0, [config.cwd].concat(config.nodeModulesPaths, [name]));
        var required = require.resolve(maybe);
        return [{
                rawInput: name,
                parsed: path_1.parse(required),
                resolved: required,
                relative: path_1.relative(config.cwd, required),
                errors: []
            }];
    }
    catch (e) {
        if (e.code !== 'MODULE_NOT_FOUND') {
            throw e;
        }
        debug("lookup for " + name + " failed", e.message);
        return [];
    }
}
// /**
//  * Look at an object of any depth and perform string substitutions
//  * from things like {paths.root}
//  * @param {Object} item
//  * @param {Object} root
//  * @returns {Object}
//  */
// const traverse = require('traverse');
// export function transformStrings(item, root) {
//     return traverse(item).map(function () {
//         if (this.isLeaf) {
//             if (typeof this.node === 'string') {
//                 this.update(replaceOne(this.node, root));
//             }
//             this.update(this.node);
//         }
//     });
// }
var traverse = require('traverse');
/**
 * Convert a JS object into ENV vars
 * eg:
 *    var obj = {
 *      options: {
 *        docker: {
 *          port: 8000
 *        }
 *      }
 *    }
 * ->
 *    envifyObject(obj, 'CB', 'OPTIONS')
 * ->
 *    CB_OPTIONS_DOCKER_PORT=8000
 */
function envifyObject(object, prefix, objectKeyName) {
    var subject = _.cloneDeep(object);
    return traverse(subject).reduce(function (acc, x) {
        if (this.level > 4) {
            this.remove();
            return acc;
        }
        if (this.circular) {
            this.remove();
            return acc;
        }
        if (this.isLeaf) {
            acc[[prefix, objectKeyName].concat(this.path).join('_').toUpperCase()] = String(this.node);
        }
        return acc;
    }, {});
}
exports.envifyObject = envifyObject;
var merge = require('../lodash.custom').merge;
function excludeKeys(input, blacklist) {
    return Object.keys(input).filter(function (x) { return blacklist.indexOf(x) === -1; }).reduce(function (acc, key) {
        acc[key] = input[key];
        return acc;
    }, {});
}
exports.excludeKeys = excludeKeys;
/**
 * Currently we add the following from the toplevel of inputs
 * 1. options
 * 2. config
 * 3. CLI trailing args + command
 * 4. env
 */
var configBlacklist = ['outputObserver', 'fileChangeObserver', 'signalObserver', 'scheduler'];
function getCBEnv(trigger) {
    var prefix = trigger.config.envPrefix;
    // 1. Crossbow options (from cbfile etc)
    var cbOptionsEnv = envifyObject(trigger.input.options, prefix, 'options');
    // 2. Crossbow config (from config key or CLI flags)
    var cbConfigEnv = envifyObject(excludeKeys(trigger.config, configBlacklist), prefix, 'config');
    // 3. command + trailing cli args
    var _a = trigger.cli, trailing = _a.trailing, command = _a.command;
    var cbCliEnv = envifyObject({ trailing: trailing, command: command }, prefix, 'cli');
    // 4. env key from input file
    return merge(cbOptionsEnv, cbConfigEnv, cbCliEnv, trigger.input.env);
}
exports.getCBEnv = getCBEnv;
/**
 * @param {String} item - the string to replace
 * @param {Object} root - Root object used for lookups
 * @returns {*}
 */
function replaceOne(item, root) {
    return item.replace(/\{\{(.+?)\}\}/g, function () {
        var match = _.get(root, arguments[1].split('.'));
        if (typeof match === 'string') {
            return replaceOne(match, root);
        }
        return match;
    });
}
function getFunctionName(fn) {
    if (fn.name !== '') {
        return "[Function: " + fn.name + "]";
    }
    return '[Function]';
}
exports.getFunctionName = getFunctionName;
exports.removeNewlines = function (x) { return x.replace(/\n|\r/g, ' ').trim(); };
exports.escapeNewLines = function (x) { return x.replace(/\n|\r/g, '\\n').trim(); };
exports.removeTrailingNewlines = function (x) { return x.replace(/(\n|\r)$/, ' ').trim(); };
function stringifyObj(incoming, max) {
    if (max === void 0) { max = 100; }
    var asString = (function () {
        if (typeof incoming !== 'string') {
            return JSON.stringify(incoming);
        }
        return incoming;
    })();
    if (asString.length > max || asString) {
        return asString.slice(0, (max - 3)) + (function () {
            if (asString.length - max > -3)
                return '...';
            return '';
        })();
    }
    if (asString.length > process.stdout.columns) {
        return asString.slice(0, process.stdout.columns - 3) + '...';
    }
    return asString;
}
exports.stringifyObj = stringifyObj;
var toStringTypes = {
    'obj': '[object Object]',
    'string': '[object String]',
    'array': '[object Array]',
    'function': '[object Function]'
};
function testType(com, val) {
    return Object.prototype.toString.call(val) === com;
}
function isPlainObject(val) {
    return testType(toStringTypes['obj'], val);
}
exports.isPlainObject = isPlainObject;
function isString(val) {
    return testType(toStringTypes['string'], val);
}
exports.isString = isString;
function isFunction(val) {
    return testType(toStringTypes['function'], val);
}
exports.isFunction = isFunction;
function isReport(report) {
    return report && isString(report.type) &&
        report.type === task_runner_1.TaskReportType.start ||
        report.type === task_runner_1.TaskReportType.end ||
        report.type === task_runner_1.TaskReportType.error;
}
exports.isReport = isReport;
function isPrivateTask(taskName) {
    return taskName[0] === '_';
}
exports.isPrivateTask = isPrivateTask;
function isPublicTask(taskName) {
    return taskName[0] !== '_';
}
exports.isPublicTask = isPublicTask;
function isInternal(incoming) {
    return /_internal_fn_\d{0,10}$/.test(incoming);
}
exports.isInternal = isInternal;
var supportedFileExtensions = ['.js'];
function isSupportedFileType(incoming) {
    return supportedFileExtensions.indexOf(incoming.toLowerCase()) > -1;
}
exports.isSupportedFileType = isSupportedFileType;
function _e(x) {
    return x
        .replace(/\n|\r/g, '')
        .replace(/\{/g, '\\\{')
        .replace(/}/g, '\\\}');
}
exports._e = _e;
function __e(x) {
    return x
        .replace(/\{/g, '\\\{')
        .replace(/}/g, '\\\}');
}
exports.__e = __e;
function longestString(col) {
    return col.reduce(function (val, item) { return item.length > val ? item.length : val; }, 0);
}
exports.longestString = longestString;
function padLine(incoming, max) {
    if (incoming.length <= max) {
        return incoming + new Array(max - incoming.length + 1).join(' ');
    }
    return incoming;
}
exports.padLine = padLine;
function concatProps(tasks, initial, propname) {
    return tasks.reduce(function (acc, task) {
        if (task.tasks.length) {
            return acc.concat(concatProps(task.tasks, [], propname));
        }
        if (task[propname].length)
            return acc.concat(task[propname]);
        return acc;
    }, initial);
}
exports.concatProps = concatProps;
//# sourceMappingURL=task.utils.js.map