"use strict";
var cli_parse_1 = require('./cli.parse');
var task_resolve_1 = require("./task.resolve");
var task_utils_1 = require("./task.utils");
var task_errors_1 = require("./task.errors");
var _ = require('../lodash.custom');
var qs = require('qs');
var flagRegex = /(.+?)@(.+)?$/;
var inlineFnCount = 0;
function preprocessTask(taskName, trigger, parents) {
    var output = (function () {
        if (typeof taskName === 'function') {
            return handleFunctionInput(taskName, trigger.input, parents);
        }
        if (typeof taskName === 'string') {
            return handleStringInput(taskName, trigger.input, parents);
        }
        if (task_utils_1.isPlainObject(taskName)) {
            return handleObjectInput(taskName, trigger.input, parents);
        }
        if (Array.isArray(taskName)) {
            return handleArrayInput(taskName, trigger.input, parents);
        }
    })();
    /**
     * Mark this item as 'skipped' if the task name matches
     * one given in the --skip flag via CLI
     *
     * Note: this is separate to individual task config
     *
     *  eg:
     *      crossbow run deploy --skip build-js
     *
     *  -> All tasks under deploy still run, but build-js will be skipped
     */
    if (trigger.config.skip.length) {
        if (trigger.config.skip.indexOf(output.baseTaskName) > -1) {
            output.skipped = true;
        }
    }
    return output;
}
exports.preprocessTask = preprocessTask;
var objectCount = 0;
function handleObjectInput(taskLiteral, input, parents) {
    if (taskLiteral.tasks && taskLiteral.tasks.length) {
        var name_1 = 'AnonObject_' + objectCount++;
        var out = task_resolve_1.createTask(_.assign({
            baseTaskName: name_1,
            taskName: name_1,
            rawInput: JSON.stringify(taskLiteral),
            valid: true,
            parents: parents,
            origin: task_resolve_1.TaskOriginTypes.InlineObject,
            type: task_resolve_1.TaskTypes.TaskGroup
        }, taskLiteral));
        return out;
    }
    if (typeof taskLiteral.input === 'string') {
        return stubAdaptor(taskLiteral.input, taskLiteral, parents);
    }
    if (typeof taskLiteral.adaptor === 'string' && typeof taskLiteral.command === 'string') {
        taskLiteral.adaptor = taskLiteral.adaptor.replace(/^@/, '');
        return stubAdaptor("@" + taskLiteral.adaptor + " " + taskLiteral.command, taskLiteral, parents);
    }
    return task_resolve_1.createTask({
        rawInput: task_utils_1.stringifyObj(taskLiteral),
        taskName: '',
        type: task_resolve_1.TaskTypes.Adaptor,
        origin: task_resolve_1.TaskOriginTypes.Adaptor,
        adaptor: '',
        errors: [{
                type: task_errors_1.TaskErrorTypes.InvalidTaskInput,
                input: taskLiteral
            }]
    });
}
exports.handleObjectInput = handleObjectInput;
function handleArrayInput(taskItems, input, parents) {
    var name = 'AnonGroup_' + taskItems.join(',').slice(0, 10) + '...';
    return task_resolve_1.createTask({
        runMode: task_resolve_1.TaskRunModes.parallel,
        baseTaskName: name,
        taskName: name,
        rawInput: taskItems.toString(),
        valid: true,
        parents: parents,
        origin: task_resolve_1.TaskOriginTypes.InlineArray,
        type: task_resolve_1.TaskTypes.TaskGroup,
        tasks: taskItems
    });
}
exports.handleArrayInput = handleArrayInput;
function stubAdaptor(string, taskLiteral, parents) {
    var taskLiteralAdaptor = task_resolve_1.createAdaptorTask(string, parents);
    return _.assign({}, taskLiteralAdaptor, taskLiteral);
}
/**
 * String can be given that may be task themselves, like NPM tasks
 * or shell commands, but they can also be an alias for other tasks
 *
 * examples:
 *
 *  - @npm webpack --config webpack.dev.js
 *  - build (which may be an alias for many other tasks)
 */
function handleStringInput(taskName, input, parents) {
    /**
     * Never modify the current task if it begins
     * with a `@` - instead just return early with
     * a adaptors task
     *  eg: `@npm webpack`
     */
    if (taskName.match(/^@/)) {
        return task_resolve_1.createAdaptorTask(taskName, parents);
    }
    /**
     * Split any end cbflags from the main task name
     * @type {SplitTaskAndFlags}
     */
    var split = getSplitFlags(taskName, input);
    /**
     * Split the incoming taskname on colons
     *  eg: sass:site:dev
     *  ->  ['sass', 'site', 'dev']
     * @type {Array}
     */
    var splitTask = split.taskName.split(':');
    /**
     * Take the first (or the only) item as the base task name
     *  eg: uglify:*
     *  ->  'uglify'
     * @type {string}
     */
    var baseTaskName = splitTask[0];
    var subTasks = splitTask.slice(1);
    /**
     * Create the base task
     * @type {IncomingTask}
     */
    var incomingTask = task_resolve_1.createTask({
        cbflags: split.cbflags,
        query: split.query,
        flags: split.flags,
        baseTaskName: baseTaskName,
        subTasks: subTasks,
        taskName: baseTaskName,
        rawInput: taskName
    });
    /**
     * Now pass it off to allow any flags to applied
     */
    return processFlags(incomingTask);
}
/**
 * Function can be given inline so this methods handles that
 */
function handleFunctionInput(taskName, input, parents) {
    var fnName = taskName['name'];
    var identifier = ("_inline_fn_" + inlineFnCount++ + "_") + fnName;
    return task_resolve_1.createTask({
        runMode: task_resolve_1.TaskRunModes.series,
        baseTaskName: identifier,
        taskName: identifier,
        rawInput: identifier,
        inlineFunctions: [taskName],
        valid: true,
        parents: parents,
        origin: task_resolve_1.TaskOriginTypes.InlineFunction,
        type: task_resolve_1.TaskTypes.InlineFunction
    });
}
/**
 *
 */
function getSplitFlags(taskName, input) {
    /**
     * Split up the task name from any flags/queries/cbflags etc
     * @type {{baseName: string, flags: {}}}
     */
    var baseNameAndFlags = getBaseNameAndFlags(taskName);
    /**
     * Split tasks based on whether or not they have flags
     *    eg: crossbow run '@npm run webpack@p'
     *    ->  taskName: '@npm run webpack'
     *    ->  cbflags: ['p']
     * @type {RegExpMatchArray}
     */
    var splitCBFlags = baseNameAndFlags.baseName.match(flagRegex);
    /**
     * If splitFlags is falsey, there was no flag so return
     * an empty array and the full task name
     */
    if (!splitCBFlags) {
        var splitQuery_1 = baseNameAndFlags.baseName.split('?');
        var query_1 = getQuery(splitQuery_1);
        /**
         * Next, look at the top-level input,
         * is this taskname going to match, and if so, does it contain any flags?
         */
        var cbflags_1 = Object.keys(input.tasks).reduce(function (all, key) {
            var match = key.match(new RegExp("^" + taskName + "@(.+)"));
            if (match) {
                return all.concat(match[1].split(''));
            }
            return all;
        }, []);
        return { taskName: splitQuery_1[0], query: query_1, cbflags: cbflags_1, flags: baseNameAndFlags.flags };
    }
    /**
     * At this point, there was at LEAST an @ at the end of the task name
     * @type {string}
     */
    var base = splitCBFlags[1];
    var splitQuery = base.split('?');
    var query = getQuery(splitQuery);
    var cbflags = (function () {
        /**
         * If the 3rd item in the regex match is undefined, it means
         * the @ was used at the end of the task name, but a value was not given.
         * In that case we return an empty string to allow the error collection
         * to kick in later
         * @type {string[]}
         */
        if (splitCBFlags[2] === undefined) {
            return [''];
        }
        /**
         * Default case is where there are chars after the @, so we split them up
         *   eg: crossbow run '@npm run webpack@pas'
         *   ->  flags: ['p', 'a', 's']
         */
        return splitCBFlags[2].split('');
    })();
    return {
        taskName: splitQuery[0],
        query: query,
        cbflags: cbflags,
        flags: baseNameAndFlags.flags
    };
}
function getQuery(splitQuery) {
    if (splitQuery.length > 1) {
        return qs.parse(splitQuery[1]);
    }
    return {};
}
/**
 * Apply any transformations to options based on
 * CB flags
 * @param task
 * @returns {any}
 */
function processFlags(task) {
    var runMode = (function () {
        if (task.cbflags.indexOf('p') > -1) {
            return task_resolve_1.TaskRunModes.parallel;
        }
        return task_resolve_1.TaskRunModes.series;
    })();
    return _.assign({}, task, {
        runMode: runMode
    });
}
/**
 * Split basename + opts
 * @param taskName
 * @returns {{baseName: any, flags: {}}}
 */
function getBaseNameAndFlags(taskName) {
    var splitFlags = taskName.trim().split(/^(.+?) /);
    /**
     * Basename is everything upto the first space
     * @type {string}
     */
    var baseName = (function () {
        if (splitFlags.length === 1) {
            return splitFlags[0];
        }
        return splitFlags[1];
    })();
    /**
     * Flags is an object containing anything after the first space,
     * parsed as CLI input
     * @type {Flags|{}}
     */
    var flags = (function () {
        if (splitFlags.length === 3) {
            return cli_parse_1.default(splitFlags[1] + ' ' + splitFlags[2]).flags;
        }
        return {};
    })();
    return { baseName: baseName, flags: flags };
}
//# sourceMappingURL=task.preprocess.js.map