"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
var rxjs_1 = require("rxjs");
var logger_1 = require("../logger");
var logger = logger_1.default('TaskQueue');
var TaskQueue = /** @class */ (function () {
    function TaskQueue() {
        this.tasks = [];
    }
    Object.defineProperty(TaskQueue.prototype, "workingState", {
        set: function (v) {
            this._subscription = v;
            // logger.debug(`state=${this._subscription !== undefined ? 'WORKING' : 'IDLE'}`);
            if (this._subscription === undefined) {
                this.processNext();
                this.currentTask = undefined;
            }
        },
        enumerable: true,
        configurable: true
    });
    TaskQueue.prototype.addExecutor = function (executor) {
        return this.addTask(new CancelableTask(executor));
    };
    TaskQueue.prototype.addTask = function (task) {
        this.tasks.push(task);
        // logger.debug(`addTask... total ${this.tasks.length}`)
        this.processNext();
        return task.toObservable();
    };
    TaskQueue.prototype.cancelAll = function (withError) {
        if (this.currentTask) {
            this.currentTask.cancel(withError);
        }
        var task = this.tasks.shift();
        while (task) {
            task.cancel(withError);
            task = this.tasks.shift();
        }
    };
    TaskQueue.prototype.processNext = function () {
        var _this = this;
        // logger.debug(`Processing next task... total ${this.tasks.length} state=${this._subscription !== undefined ? 'WORKING' : 'IDLE'}`)
        if (this._subscription === undefined) {
            var task = this.tasks.shift();
            if (task) {
                // logger.debug(`Processing next task... remaining ${this.tasks.length}`)
                this.currentTask = task;
                var isDone_1 = false;
                var observable = task.execute();
                var subscription = observable
                    .subscribe({
                    next: function () {
                        // logger.debug(`Task done successful!`)
                        _this.workingState = undefined;
                        isDone_1 = true;
                    },
                    error: function (err) {
                        logger.debug("Task done with error!", err);
                        _this.workingState = undefined;
                        isDone_1 = true;
                    }
                });
                if (!isDone_1) {
                    this.workingState = subscription;
                }
            }
        }
        else {
            logger.debug("addTask... there is already a process running...");
        }
    };
    return TaskQueue;
}());
exports.TaskQueue = TaskQueue;
var TaskOperationCanceled = /** @class */ (function (_super) {
    __extends(TaskOperationCanceled, _super);
    function TaskOperationCanceled(task) {
        var _this = _super.call(this, "Operation canceled") || this;
        _this.task = task;
        _this.code = "OperationCanceled";
        return _this;
    }
    return TaskOperationCanceled;
}(Error));
exports.TaskOperationCanceled = TaskOperationCanceled;
var CancelableTask = /** @class */ (function () {
    function CancelableTask(operation) {
        this.operation = operation;
        this._subject = new rxjs_1.Subject();
        this._canceled = false;
        this._executed = false;
    }
    Object.defineProperty(CancelableTask.prototype, "isCanceled", {
        get: function () { return this._canceled; },
        enumerable: true,
        configurable: true
    });
    ;
    /**
     * If canceled is called before the execute => do nothing
     * otherwise reject pending request
     * If canceled is called more than once, do nothing
     */
    CancelableTask.prototype.cancel = function (withError) {
        if (!this._canceled) {
            logger.debug("Canceling operation");
            this._canceled = true;
            var error = withError || new TaskOperationCanceled(this);
            this._subject.error(error);
        }
    };
    /**
     * If execute is called after cancel => return operation canceled exception
     * If already executing, return the current execution promise
     */
    CancelableTask.prototype.execute = function () {
        var _this = this;
        if (!this._executed && !this._canceled) {
            this._executed = true;
            logger.debug("Start operation execution...");
            this.operation().subscribe({
                next: function (data) {
                    // logger.debug(`NEXT `, data);
                    _this._subject.next(data);
                },
                error: function (err) {
                    _this._subject.error(err);
                },
                complete: function () {
                    // logger.debug(`COMPLETE! `);
                    _this._subject.complete();
                }
            });
        }
        // else {
        //     logger.debug(`Task is either canceled or already executed...`);
        // }
        return this._subject;
    };
    CancelableTask.prototype.toObservable = function () {
        return this._subject;
    };
    return CancelableTask;
}());
exports.CancelableTask = CancelableTask;
