#!/usr/bin/env node
"use strict";
var config_1 = require('./config');
var file_utils_1 = require('./file.utils');
var input_resolve_1 = require("./input.resolve");
var reports = require("./reporter.resolve");
var Rx = require('rx');
var _ = require('../lodash.custom');
var debug = require('debug')('cb:init');
var availableCommands = {
    run: './command.run',
    r: './command.run',
    tasks: './command.tasks',
    t: './command.tasks',
    ls: './command.tasks',
    watch: './command.watch',
    w: './command.watch',
    watchers: './command.watchers',
    init: './command.init',
    docs: './command.docs',
};
var isCommand = function (input) { return Object.keys(availableCommands).indexOf(input) > -1; };
/**
 * Handle any type of init. It could be from the CLI, or via the API.
 * eg, any command from the CLI ultimately ends up in the following call
 *    $  crossbow run task1 -c conf/cb.js
 *    -> handleIncoming({
 *          input: ['run', 'task1'],
 *          flags: {c: 'conf/cb.js'}
 *       });
 */
function prepareInput(cli, input, outputObserver, signalObserver) {
    var mergedConfig = config_1.merge(cli.flags);
    var userInput = input_resolve_1.getInputs(mergedConfig, input);
    var resolvedReporters = reports.getReporters(mergedConfig, input);
    var chosenOutputObserver = reports.getOutputObserver(mergedConfig, outputObserver);
    var chosenSignalObserver = reports.getSignalReporter(mergedConfig, signalObserver);
    var hasReporters = resolvedReporters.valid.length;
    var defaultReporter = reports.getDefaultReporter();
    // Check if any given reporter are invalid
    // and defer to default
    if (resolvedReporters.invalid.length) {
        defaultReporter({
            type: reports.ReportTypes.InvalidReporter,
            data: {
                reporters: resolvedReporters
            }
        }, chosenOutputObserver);
        return {
            userInput: userInput,
            cli: cli,
            config: mergedConfig,
            errors: [{ type: reports.ReportTypes.InvalidReporter }]
        };
    }
    // proxy for calling reporter functions.
    // uses default if none given
    var reportFn = function (report) {
        if (!hasReporters) {
            return defaultReporter(report, chosenOutputObserver);
        }
        resolvedReporters.valid.forEach(function (reporter) {
            reporter.callable(report, chosenOutputObserver);
        });
    };
    // Bail early if a user tried to load a specific file
    // but it didn't exist, or had some other error
    if (userInput.errors.length) {
        reportFn({ type: reports.ReportTypes.InputError, data: userInput });
        return {
            userInput: userInput,
            cli: cli,
            config: mergedConfig,
            reportFn: reportFn,
            errors: [{ type: reports.ReportTypes.InputError }]
        };
    }
    // at this point, there are no invalid reporters or input files
    // so we can reset the reporters to anything that may of come in via config
    if (userInput.inputs.length) {
        mergedConfig = config_1.merge(_.merge({}, userInput.inputs[0].config, cli.flags));
    }
    resolvedReporters = reports.getReporters(mergedConfig, input);
    hasReporters = resolvedReporters.valid.length;
    /**
     * Set the signal observer
     * todo: Clean up how this is assigned
     * @type {OutgoingSignals}
     */
    mergedConfig.signalObserver = chosenSignalObserver;
    // Check if any given reporter are invalid
    // and defer to default (again)
    if (resolvedReporters.invalid.length) {
        reportFn({ type: reports.ReportTypes.InvalidReporter, data: { reporters: resolvedReporters } });
        return {
            userInput: userInput,
            cli: cli,
            reportFn: reportFn,
            config: mergedConfig,
            errors: [{ type: reports.ReportTypes.InvalidReporter }]
        };
    }
    // Show the user which external inputs are being used
    if (userInput.type === input_resolve_1.InputTypes.ExternalFile ||
        userInput.type === input_resolve_1.InputTypes.CBFile ||
        userInput.type === input_resolve_1.InputTypes.DefaultExternalFile)
        reportFn({ type: reports.ReportTypes.UsingInputFile, data: { sources: userInput.sources } });
    return {
        userInput: userInput,
        cli: cli,
        reportFn: reportFn,
        config: mergedConfig,
        errors: []
    };
}
exports.prepareInput = prepareInput;
/**
 * This the the proxy that allows command/run mode to be handled
 * @param preparedInput
 */
function handleIncoming(preparedInput) {
    var cli = preparedInput.cli, userInput = preparedInput.userInput, config = preparedInput.config, reportFn = preparedInput.reportFn;
    // if the user provided a --cbfile flag, the type 'CBFile'
    // must be available, otherwise this is an error state
    if (userInput.type === input_resolve_1.InputTypes.CBFile) {
        return handleCBfileMode(cli, config, reportFn);
    }
    return processInput(cli, userInput.inputs[0], config, reportFn);
}
exports.handleIncoming = handleIncoming;
function handleCBfileMode(cli, config, reportFn) {
    var createFilePaths = file_utils_1.getRequirePaths(config);
    var input = require(createFilePaths.valid[0].resolved);
    input.default.config = processConfigs(_.merge({}, config, input.default.config), cli.flags);
    input.default.cli = cli;
    input.default.reporter = reportFn;
    if (isCommand(cli.input[0])) {
        return require(availableCommands[cli.input[0]]).default.call(null, cli, input.default, input.default.config, reportFn);
    }
    cli.input = ['run'].concat(cli.input);
    return require(availableCommands['run']).default.call(null, cli, input.default, input.default.config, reportFn);
}
/**
 * Now decide who should handle the current command
 */
function processInput(cli, input, config, reportFn) {
    var firstArg = cli.input[0];
    return require(availableCommands[firstArg]).default.call(null, cli, input, config, reportFn);
}
function processConfigs(config, flags) {
    var cbConfig = _.merge({}, config, flags);
    return config_1.merge(cbConfig);
}
/**
 * This is the default export that can be
 * used as a convenience method.
 * Note: types are lost when using this method.
 */
function default_1(cli, input) {
    var prepared = prepareInput(cli, input);
    if (prepared.errors.length) {
        return Rx.Observable.just({
            errors: prepared.errors
        });
    }
    return handleIncoming(prepared);
}
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = default_1;
//# sourceMappingURL=index.js.map