"use strict";
var seq = require('./task.sequence');
var task_runner_1 = require("./task.runner");
var Rx = require("rx");
var reporter_resolve_1 = require("./reporter.resolve");
var command_watch_1 = require("./command.watch");
var debug = require('debug')('cb:watch.runner');
/**
 * Create a stream that is the combination of all watchers
 */
function createObservablesForWatchers(watchers, trigger) {
    /**
     * Wrap every chokidar watcher in an observable
     * @type {Rx.Observable<WatchEvent>[]}
     */
    var watchersAsObservables = watchers.map(function (watcher) {
        return createObservableForWatcher(watcher, trigger);
    });
    var blockable$ = new Rx.BehaviorSubject([]);
    /**
     * Now map file-change events to task running
     */
    return (_a = Rx.Observable).merge.apply(_a, watchersAsObservables)
        .map(function (watchEvent) {
        var watcher = watchers.filter(function (x) { return x.watcherUID === watchEvent.watcherUID; })[0];
        return { watchEvent: watchEvent, watcher: watcher };
    })
        .filter(function (x) {
        if (x.watcher.options.block) {
            var blocked = !~blockable$.getValue().indexOf(x.watchEvent.watcherUID);
            debug("BLOCKED - " + x.watchEvent.watcherUID + " " + x.watchEvent.path + " " + x.watchEvent.event);
            return blocked;
        }
        return true;
    })
        .do(function (x) {
        if (x.watcher.options.block) {
            blockable$.onNext(blockable$.getValue().concat(x.watchEvent.watcherUID));
        }
    })
        .timestamp(trigger.config.scheduler)
        .flatMap(function (incoming, i) {
        return runTasks(incoming, i);
    });
    function runTasks(incoming, i) {
        /**
         * @type {WatchEvent}
         */
        var _a = incoming.value, watchEvent = _a.watchEvent, watcher = _a.watcher;
        return Rx.Observable.create(function (obs) {
            /**
             * Report start of task run
             */
            trigger.reporter({
                type: reporter_resolve_1.ReportTypes.WatcherTriggeredTasks,
                data: {
                    index: i,
                    taskCollection: watcher.tasks
                }
            });
            /**
             * todo: Is there a way to handle this without subscribing manually?
             */
            watcher._runner.series()
                .do(function (taskReport) { return obs.onNext({
                type: command_watch_1.WatchCommandEventTypes.WatchTaskReport,
                data: {
                    taskReport: taskReport,
                    watchEvent: watchEvent,
                    count: i
                }
            }); })
                .toArray()
                .timestamp(trigger.config.scheduler)
                .subscribe(function (x) {
                var reports = x.value;
                var sequence = seq.decorateSequenceWithReports(watcher._sequence, reports);
                var errors = reports.filter(function (x) { return x.type === task_runner_1.TaskReportType.error; });
                obs.onNext({
                    type: command_watch_1.WatchCommandEventTypes.WatchRunnerComplete,
                    data: {
                        sequence: sequence,
                        reports: reports,
                        errors: errors,
                        watchEvent: watchEvent,
                        runtime: x.timestamp - incoming.timestamp
                    }
                });
                if (errors.length > 0) {
                    trigger.reporter({
                        type: reporter_resolve_1.ReportTypes.WatcherSummary,
                        data: {
                            sequence: sequence,
                            cli: trigger.cli,
                            title: watcher.tasks.join(', '),
                            config: trigger.config,
                            runtime: x.timestamp - incoming.timestamp,
                            watcher: watcher,
                            watchEvent: watchEvent
                        }
                    });
                }
                else {
                    trigger.reporter({
                        type: reporter_resolve_1.ReportTypes.WatcherTriggeredTasksCompleted,
                        data: {
                            index: i,
                            taskCollection: watcher.tasks,
                            time: x.timestamp - incoming.timestamp
                        }
                    });
                }
                var withoutThis = blockable$.getValue().filter(function (x) { return x !== watchEvent.watcherUID; });
                blockable$.onNext(withoutThis);
                obs.onCompleted();
            });
        });
    }
    var _a;
}
exports.createObservablesForWatchers = createObservablesForWatchers;
/**
 * Create a file-system watcher that will emit <WatchEvent>
 */
function createObservableForWatcher(watcher, trigger) {
    var reporter = trigger.reporter;
    var scheduler = trigger.config.scheduler;
    /**
     * First create a stream of file-watcher events for this Watcher
     */
    var output$ = trigger.config.fileChangeObserver || getFileChangeStream(watcher, reporter);
    /**
     * Specify a mapping from option name -> Rx.Observable operator name
     */
    var additionalOperators = [
        {
            option: 'debounce',
            fnName: 'debounce'
        },
        {
            option: 'throttle',
            fnName: 'throttle'
        },
        {
            option: 'delay',
            fnName: 'delay'
        }
    ];
    return applyOperators(output$, additionalOperators, watcher.options, scheduler);
}
exports.createObservableForWatcher = createObservableForWatcher;
function getFileChangeStream(watcher, reporter) {
    /** DEBUG **/
    debug("[id:" + watcher.watcherUID + "] options: " + JSON.stringify(watcher.options, null, 2));
    /** DEBUG END **/
    return Rx.Observable.create(function (observer) {
        /** DEBUG **/
        debug("+ [id:" + watcher.watcherUID + "] " + watcher.patterns.length + " patterns (" + watcher.patterns + ")");
        debug("\u2514\u2500 " + watcher.tasks.length + " tasks (" + watcher.tasks + ")");
        /** DEBUG END **/
        var chokidarWatcher = require('chokidar').watch(watcher.patterns, watcher.options)
            .on('all', function (event, path) {
            debug("\u2514\u2500 CHOKIDAR EVENT " + event + " - " + path);
            observer.onNext({
                event: event,
                path: path,
                watcherUID: watcher.watcherUID
            });
        });
        chokidarWatcher.on('ready', function () {
            /** DEBUG **/
            debug("\u221A [id:" + watcher.watcherUID + "] watcher ready (" + watcher.patterns + ")");
            /** DEBUG END **/
            if (Object.keys(chokidarWatcher.getWatched()).length === 0) {
                reporter({ type: reporter_resolve_1.ReportTypes.NoFilesMatched, data: { watcher: watcher } });
            }
        });
        return function () {
            debug("- for " + watcher.patterns);
            chokidarWatcher.close();
        };
    });
}
exports.getFileChangeStream = getFileChangeStream;
/**
 *
 */
function applyOperators(source, items, options, scheduler) {
    return items.reduce(function (stream$, item) {
        var value = options[item.option];
        if (value !== undefined && value > 0) {
            return stream$[item.fnName].call(stream$, value, scheduler);
        }
        return stream$;
    }, source);
}
//# sourceMappingURL=watch.file-watcher.js.map