"use strict";
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 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 task_runner_1 = require("./task.runner");
var seq = require("./task.sequence");
var debug = require('debug')('cb:command.watch');
var _ = require('../lodash.custom');
(function (WatchCommandEventTypes) {
    WatchCommandEventTypes[WatchCommandEventTypes["SetupError"] = 'SetupError'] = "SetupError";
    WatchCommandEventTypes[WatchCommandEventTypes["FileEvent"] = 'FileEvent'] = "FileEvent";
    WatchCommandEventTypes[WatchCommandEventTypes["WatchTaskReport"] = 'WatchTaskReport'] = "WatchTaskReport";
    WatchCommandEventTypes[WatchCommandEventTypes["WatchRunnerComplete"] = 'WatchRunnerComplete'] = "WatchRunnerComplete";
    WatchCommandEventTypes[WatchCommandEventTypes["BeforeTasksComplete"] = 'BeforeTasksComplete'] = "BeforeTasksComplete";
})(exports.WatchCommandEventTypes || (exports.WatchCommandEventTypes = {}));
var WatchCommandEventTypes = exports.WatchCommandEventTypes;
function executeWatchCommand(trigger) {
    var cli = trigger.cli, input = trigger.input, config = trigger.config, reporter = trigger.reporter;
    var _a = getWatchCommandSetup(trigger), beforeTasks = _a.beforeTasks, watchTasks = _a.watchTasks, watchRunners = _a.watchRunners;
    /**
     * Never continue if any BEFORE tasks were flagged as invalid
     */
    if (beforeTasks.tasks.invalid.length) {
        reporter({ type: reporter_resolve_1.ReportTypes.BeforeWatchTaskErrors, data: { watchTasks: watchTasks, trigger: trigger } });
        return Rx.Observable.just({
            setup: {
                watchTasks: watchTasks,
                watchRunners: watchRunners,
                beforeTasks: beforeTasks,
                errors: [{ type: reporter_resolve_1.ReportTypes.BeforeWatchTaskErrors, data: { watchTasks: watchTasks, trigger: trigger } }]
            },
            update$: Rx.Observable.empty()
        });
    }
    /**
     * Never continue if any tasks were flagged as
     * // todo, how do we get here
     */
    if (watchTasks.invalid.length) {
        reporter({ type: reporter_resolve_1.ReportTypes.WatchTaskErrors, data: { watchTasks: watchTasks.all, cli: cli, input: input } });
        return Rx.Observable.just({
            setup: {
                watchTasks: watchTasks,
                watchRunners: watchRunners,
                beforeTasks: beforeTasks,
                errors: [{ type: reporter_resolve_1.ReportTypes.WatchTaskErrors, data: { watchTasks: watchTasks, trigger: trigger } }]
            },
            update$: Rx.Observable.empty()
        });
    }
    /**
     * Never continue if any runners are invalid
     */
    if (watchRunners.invalid.length) {
        watchRunners.invalid.forEach(function (runner) {
            reporter({ type: reporter_resolve_1.ReportTypes.WatchTaskTasksErrors, data: { tasks: runner._tasks.all, runner: runner, config: config } });
        });
        return Rx.Observable.just({
            setup: {
                watchTasks: watchTasks,
                watchRunners: watchRunners,
                beforeTasks: beforeTasks,
                errors: [{ type: reporter_resolve_1.ReportTypes.WatchTaskTasksErrors }]
            },
            update$: Rx.Observable.empty()
        });
    }
    /**
     * If there are no before tasks to execute, just begin the watchers
     */
    if (!beforeTasks.tasks.valid.length) {
        reporter({ type: reporter_resolve_1.ReportTypes.Watchers, data: { watchTasks: watchTasks.valid, config: config } });
        return Rx.Observable.just({
            setup: {
                watchTasks: watchTasks,
                watchRunners: watchRunners,
                beforeTasks: beforeTasks,
                errors: []
            },
            update$: watch_file_watcher_1.createObservablesForWatchers(watchRunners.valid, trigger)
        });
    }
    reporter({ type: reporter_resolve_1.ReportTypes.BeforeTaskList, data: { sequence: beforeTasks.sequence, cli: cli, config: trigger.config } });
    var withBefore$ = Rx.Observable.zip(
    /**
     * Timestamp the beginning
     */
    Rx.Observable.just(true).timestamp(config.scheduler).map(function (x) { return x.timestamp; }), 
    /**
     * Run the tasks
     */
    beforeTasks.runner.series().toArray().timestamp(config.scheduler), 
    /**
     * Combine the start time + report from the runner
     */
    function (start, x) {
        var reports = x.value;
        var endtime = x.timestamp;
        return { duration: endtime - start, reports: reports };
    })
        .flatMap(function (x) {
        var duration = x.duration, reports = x.reports;
        var sequence = seq.decorateSequenceWithReports(beforeTasks.sequence, reports);
        var errors = reports.filter(function (x) { return x.type === task_runner_1.TaskReportType.error; });
        reporter({
            type: reporter_resolve_1.ReportTypes.BeforeTasksSummary,
            data: {
                sequence: sequence,
                cli: cli,
                config: config,
                runtime: duration,
                errors: errors
            }
        });
        var beforeReport = {
            type: WatchCommandEventTypes.BeforeTasksComplete,
            data: {
                reports: reports,
                errors: errors
            }
        };
        /**
         * If an error occurred, and the user did not provide --no-fail flag
         * don't continue with the watchers
         */
        if (errors.length && config.fail) {
            return Rx.Observable.just(beforeReport);
        }
        /**
         * Report running watchers
         */
        reporter({ type: reporter_resolve_1.ReportTypes.Watchers, data: { watchTasks: watchTasks.valid, config: config } });
        /**
         * Send the before report followed by the following watch task reports
         */
        return Rx.Observable.concat(Rx.Observable.just(beforeReport), watch_file_watcher_1.createObservablesForWatchers(watchRunners.valid, trigger));
    });
    return Rx.Observable.just({
        setup: {
            watchTasks: watchTasks,
            watchRunners: watchRunners,
            beforeTasks: beforeTasks,
            errors: []
        },
        update$: withBefore$
    });
}
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 executeWatchCommand(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({ type: reporter_resolve_1.ReportTypes.NoWatchersAvailable });
            return Rx.Observable.just({
                setup: {
                    errors: [{ type: reporter_resolve_1.ReportTypes.NoWatchersAvailable }]
                },
                update$: Rx.Observable.empty()
            });
        }
        reporter({ type: reporter_resolve_1.ReportTypes.NoWatchTasksProvided });
        return command_watch_interactive_1.default(cli, input, config)
            .flatMap(function (answers) {
            var cliMerged = _.merge({}, cli, { input: answers.watch });
            return executeWatchCommand({
                shared: sharedMap,
                cli: cliMerged,
                input: input,
                config: config,
                reporter: reporter,
                type: command_run_1.TriggerTypes.watcher
            });
        });
    }
    return executeWatchCommand(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;
function getWatchCommandSetup(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();
    /**
     * First Resolve the task names given in input.
     */
    var watchTasks = watch_resolve_1.resolveWatchTasks(trigger.cli.input, trigger);
    /**
     * Create runners for watch tasks;
     */
    var watchRunners = watch_runner_1.createWatchRunners(watchTasks, trigger);
    /**
     * Get a special runner that will executeWatchCommand before
     * watchers begin
     * @type {BeforeTasks}
     */
    var beforeTasks = watch_before_1.getBeforeTaskRunner(trigger, watchTasks);
    /**
     *
     */
    return { watchRunners: watchRunners, watchTasks: watchTasks, beforeTasks: beforeTasks, errors: [] };
}
exports.getWatchCommandSetup = getWatchCommandSetup;
//# sourceMappingURL=command.watch.js.map