"use strict";
/// <reference path="../typings/main.d.ts" />
var command_run_1 = require('./command.run');
var watch_runner_1 = require("./watch.runner");
var watch_resolve_1 = require('./watch.resolve');
var watch_shorthand_1 = require("./watch.shorthand");
var watch_before_1 = require("./watch.before");
var seq = require("./task.sequence");
var Rx = require('rx');
var Immutable = require('immutable');
var watch_file_watcher_1 = require("./watch.file-watcher");
var command_watch_interactive_1 = require("./command.watch.interactive");
var watch_utils_1 = require("./watch.utils");
var reporter_resolve_1 = require("./reporter.resolve");
var debug = require('debug')('cb:command.watch');
var _ = require('../lodash.custom');
function execute(trigger) {
    var cli = trigger.cli, input = trigger.input, config = trigger.config, reporter = trigger.reporter;
    /**
     * task Tracker for external observers
     * @type {Subject<T>}
     */
    trigger.tracker = new Rx.Subject();
    trigger.tracker$ = trigger.tracker.share();
    debug("Working with input [" + trigger.cli.input + "]");
    /**
     * First Resolve the task names given in input.
     */
    var watchTasks = watch_resolve_1.resolveWatchTasks(trigger.cli.input, trigger);
    debug(watchTasks.valid.length + " valid task(s)");
    debug(watchTasks.invalid.length + " invalid task(s)");
    /**
     * Create runners for watch tasks;
     */
    var runners = watch_runner_1.createWatchRunners(watchTasks, trigger);
    /**
     * Get a special runner that will execute before
     * watchers begin
     * @type {BeforeTasks}
     */
    var before = watch_before_1.getBeforeTaskRunner(trigger, watchTasks);
    /**
     * Check if the user intends to handle running the tasks themselves,
     * if that's the case we give them the resolved tasks along with
     * the sequence and the primed runner
     */
    if (config.handoff) {
        debug("Handing off Watchers");
        return { tasks: watchTasks, runners: runners, before: before };
    }
    debug("Not handing off, will handle watching internally");
    /**
     * Never continue if any BEFORE tasks were flagged as invalid
     */
    if (before.tasks.invalid.length) {
        reporter(reporter_resolve_1.ReportNames.BeforeWatchTaskErrors, watchTasks, trigger);
        return;
    }
    /**
     * Never continue if any tasks were flagged as
     * // todo, how do we get here
     */
    if (watchTasks.invalid.length) {
        reporter(reporter_resolve_1.ReportNames.WatchTaskErrors, watchTasks.all, cli, input);
        return;
    }
    /**
     * Never continue if any runners are invalid
     */
    if (runners.invalid.length) {
        // it doesn't make any sense to log 'valid' tasks with
        // WatchTaskTasksErrors - either don't log them, or use a more appropriate name
        // runners.valid.forEach(runner => {
        //     reporter(ReportNames.WatchTaskTasksErrors, runner._tasks.all, runner, config)
        // });
        runners.invalid.forEach(function (runner) {
            reporter(reporter_resolve_1.ReportNames.WatchTaskTasksErrors, runner._tasks.all, runner, config);
        });
        return;
    }
    /**
     * List the tasks that must complete before any watchers begin
     */
    if (before.tasks.valid.length) {
        reporter(reporter_resolve_1.ReportNames.BeforeTaskList, before.sequence, cli, trigger.config);
    }
    /**
     * To begin the watchers, we first create a runner for the 'before' tasks.
     * If this completes (tasks complete or return true) then we continue
     * to create the file-watchers and hook up the tasks
     */
    var watcher$ = Rx.Observable.concat(
    /**
     * The 'before' runner can be `true`, complete, or throw.
     * If it throws, the login in the `do` block below will not run
     * and the watchers will not begin
     */
    createBeforeRunner(before)
        .catch(function (err) {
        // Only intercept Crossbow errors
        // otherwise just allow it to be thrown
        // For example, 'before' runner may want
        // to terminate the stream, but not with a throwable
        if (err._cb) {
            sub.dispose();
            return Rx.Observable.empty();
        }
        return Rx.Observable.throw(err);
    })
        .do(function () {
        reporter(reporter_resolve_1.ReportNames.Watchers, watchTasks.valid, config);
    }), watch_file_watcher_1.createObservablesForWatchers(runners.valid, trigger)).share();
    var sub = watcher$.subscribe();
    return {
        watcher$: watcher$,
        tracker$: trigger.tracker$
    };
    /**
     * Return an Observable that's either
     * 1. a simple boolean (no before tasks),
     * 2. a throw (which means there was some error, so watchers should not begin)
     * 3. a sequence representing a runner (which will then wait until complete)
     */
    function createBeforeRunner(before) {
        if (!before.beforeTasksAsCliInput.length) {
            return Rx.Observable.just(true);
        }
        if (before.tasks.invalid.length) {
            return Rx.Observable.throw(new Error('Before task resolution failed'));
        }
        /**
         * A generic timestamp to mark the beginning of the tasks
         * @type {number}
         */
        var beforeTimestamp = new Date().getTime();
        var report = function (seq) { return reporter(reporter_resolve_1.ReportNames.Summary, seq, cli, 'Before tasks Total:', trigger.config, new Date().getTime() - beforeTimestamp); };
        return before
            .runner
            .series()
            .do(function (report) {
            reporter(reporter_resolve_1.ReportNames.TaskReport, report, trigger);
        })
            .toArray()
            .flatMap(function (reports) {
            var incoming = seq.decorateSequenceWithReports(before.sequence, reports);
            var errorCount = seq.countSequenceErrors(incoming);
            report(incoming);
            if (errorCount > 0) {
                /**
                 * If we reach here, the 'before' task sequence did not complete
                 * so we `throw` here to ensure the upstream fails
                 */
                var cberror = new Error('Before tasks did not complete!');
                reporter(reporter_resolve_1.ReportNames.BeforeTasksDidNotComplete, cberror);
                cberror._cb = true;
                return Rx.Observable.throw(cberror);
            }
            return Rx.Observable.just(incoming);
        });
    }
}
function handleIncomingWatchCommand(cli, input, config, reporter) {
    var topLevelWatchers = watch_utils_1.stripBlacklisted(Object.keys(input.watch));
    debug('top level watchers available', topLevelWatchers);
    var sharedMap = new Rx.BehaviorSubject(Immutable.Map({}));
    /**
     * If the interactive flag was given (-i), always try
     * that first.
     */
    if (config.interactive) {
        return enterInteractive();
    }
    /**
     * If the user did not provide a watcher name
     */
    if (cli.input.length === 1) {
        if (input.watch.default !== undefined) {
            var moddedCliInput = cli.input.slice();
            cli.input = moddedCliInput.concat('default');
            return execute(watch_shorthand_1.getModifiedWatchContext({
                shared: sharedMap,
                cli: cli,
                input: input,
                config: config,
                reporter: reporter,
                type: command_run_1.TriggerTypes.watcher
            }));
        }
        return enterInteractive();
    }
    /**
     * If no watchers given, or if user has selected interactive mode,
     * show the UI for watcher selection
     */
    function enterInteractive() {
        if (!topLevelWatchers.length) {
            reporter(reporter_resolve_1.ReportNames.NoWatchersAvailable);
            return;
        }
        reporter(reporter_resolve_1.ReportNames.NoWatchTasksProvided);
        return command_watch_interactive_1.default(cli, input, config).then(function (answers) {
            var cliMerged = _.merge({}, cli, { input: answers.watch });
            return execute({
                shared: sharedMap,
                cli: cliMerged,
                input: input,
                config: config,
                reporter: reporter,
                type: command_run_1.TriggerTypes.watcher
            });
        });
    }
    return execute(watch_shorthand_1.getModifiedWatchContext({
        shared: sharedMap,
        cli: cli,
        input: input,
        config: config,
        reporter: reporter,
        type: command_run_1.TriggerTypes.watcher
    }));
}
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = handleIncomingWatchCommand;
//# sourceMappingURL=command.watch.js.map