"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TaskStep = exports.TaskObserver = exports.Task = void 0;
const promise_1 = require("@f2/promise");
const complete_1 = require("./complete");
const types_1 = require("util/types");
class Task extends promise_1.Promisify {
    _observer;
    _completable;
    onComplete;
    static from(p, rejectableOrObserver, rejectable) {
        let observer;
        if (typeof rejectableOrObserver === 'boolean') {
            rejectable = rejectableOrObserver;
        }
        else if (typeof rejectableOrObserver === 'object') {
            observer = rejectableOrObserver;
        }
        rejectable = rejectable !== false;
        return new Task(p, {
            rejectable,
            observer
        });
    }
    constructor(input, options) {
        super();
        if (!options) {
            options = {};
        }
        const rejectable = options.rejectable !== false;
        const onfullfilled = (v) => {
            this.resolve(v);
            control.complete(v);
        };
        const onrejected = (err) => {
            if (rejectable) {
                this.reject(err);
            }
            else {
                this.resolve(undefined);
            }
            control.error(err);
        };
        if (typeof input === 'function') {
            try {
                const res = input();
                const then = res.then;
                if (then) {
                    then.call(res, onfullfilled, onrejected);
                }
            }
            catch (err) {
                setTimeout(() => {
                    onrejected(err);
                });
            }
        }
        else if (input) {
            input.then(onfullfilled, onrejected);
        }
        else {
            setTimeout(onfullfilled);
        }
        this._observer = options.observer;
        const control = {};
        this._completable = new complete_1.Completable(control);
        this.onComplete = this._completable.onComplete.bind(this._completable);
    }
    onProgress(cb) {
        this._observer.onProgress(cb);
    }
}
exports.Task = Task;
;
class TaskObserver {
    _onProgressFns = [];
    _step(step, arg, eventer) {
        const _step = new TaskStep(step, arg, eventer);
        this._onProgressFns.forEach(cb => cb(_step));
        return _step;
    }
    progress(step, arg) {
        this._step(step, arg);
    }
    task(step, argOrFn, arg) {
        const events = {};
        let runner;
        if (typeof argOrFn === 'function') {
            runner = argOrFn;
        }
        else {
            arg = argOrFn;
        }
        this._step(step, arg, events);
        if (runner) {
            return new Promise((resolve, reject) => {
                const _resolve = (value) => {
                    resolve(value);
                    events.onResolve();
                };
                const _reject = (err) => {
                    reject(err);
                    events.onReject(err);
                };
                try {
                    const out = runner();
                    if ((0, types_1.isPromise)(out)) {
                        out.then(_resolve).catch(_reject);
                    }
                    else {
                        _resolve(out);
                    }
                }
                catch (err) {
                    _reject(err);
                }
            });
        }
        return {
            resolve: events.onResolve,
            reject: events.onReject
        };
    }
    onProgress(cb) {
        this._onProgressFns.push(cb);
    }
}
exports.TaskObserver = TaskObserver;
class TaskStep extends complete_1.Completable {
    step;
    arg;
    constructor(step, arg, events) {
        super();
        this.step = step;
        this.arg = arg;
        if (events) {
            events.onResolve = () => {
                this._complete();
            };
            events.onReject = (err) => {
                this.error(err);
            };
        }
        else {
            this._complete();
        }
    }
    is(step, cb) {
        if (step === this.step) {
            cb(this.arg);
            const isObj = {
                is() {
                    return isObj;
                }
            };
            return isObj;
        }
        return this;
    }
}
exports.TaskStep = TaskStep;
