"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const interface_1 = require("./interface");
class ProgressImpl {
    constructor(id) {
        this.isUser = true;
        this.lastResult = undefined;
        this.result = undefined;
        this.status = interface_1.BuildStatus.NOT_STARTED;
        this.preBuildStatus = interface_1.PreBuildStatus.UNKNOWN;
        this.preBuildResult = interface_1.PreBuildResult.YES;
        this.dependencies = [];
        this.volatile = true;
        this.revision = 0;
        this._lastError = null;
        this.listeners = [];
        this.errorListeners = [];
        this.modifiedListeners = [];
        this.id = id;
    }
    resetBuildStatus() {
        this.status = interface_1.BuildStatus.NOT_STARTED;
        this.preBuildStatus = interface_1.PreBuildStatus.UNKNOWN;
        this.listeners = [];
        this.errorListeners = [];
        this.modifiedListeners = [];
    }
    start(arb, fn) {
        if (this.status === interface_1.BuildStatus.NOT_STARTED) {
            this.status = interface_1.BuildStatus.STARTED;
            this.volatile = false;
            this.dependencies = [];
            this.lastResult = this.result;
            this.result = undefined;
            arb.start(this)
                .then(fn)
                .then((x) => this.finish(arb, x))
                .catch((e) => this.error(arb, e));
        }
        return this.finishPromise();
    }
    async halt(arb) {
        if (this.status !== interface_1.BuildStatus.STARTED)
            return;
        this.status = interface_1.BuildStatus.HALT;
        await arb.halt(this);
    }
    async unhalt(arb) {
        if (this.status !== interface_1.BuildStatus.HALT)
            return;
        this.status = interface_1.BuildStatus.STARTED;
        await arb.unhalt(this);
    }
    async finish(arb, ret) {
        await arb.end(this, null);
        this.status = interface_1.BuildStatus.FINISHED;
        this.result = ret;
        const listeners = [...this.listeners];
        for (const e of listeners)
            setTimeout(() => e(ret), 0);
    }
    async error(arb, reason) {
        await arb.end(this, reason);
        this.status = interface_1.BuildStatus.ERROR;
        this._lastError = reason;
        this.volatile = true;
        const listeners = [...this.errorListeners];
        for (const e of listeners)
            setTimeout(() => e(reason), 0);
    }
    finishPromise() {
        if (this.status === interface_1.BuildStatus.FINISHED) {
            return Promise.resolve(this.result);
        }
        else if (this.status === interface_1.BuildStatus.ERROR) {
            return Promise.reject(this._lastError);
        }
        else {
            return new Promise((resolve, reject) => {
                this.listeners.push(resolve);
                this.errorListeners.push(reject);
            });
        }
    }
    startModifiedCheck(fn) {
        if (this.preBuildStatus === interface_1.PreBuildStatus.UNKNOWN) {
            this.preBuildStatus = interface_1.PreBuildStatus.CHECKING;
            fn()
                .then((x) => this.finishModifiedCheck(x))
                .catch((e) => this.finishModifiedCheck(interface_1.PreBuildResult.YES));
        }
        return this.modifiedCheckFinishPromise();
    }
    async finishModifiedCheck(_yes) {
        if (this.preBuildStatus !== interface_1.PreBuildStatus.DECIDED) {
            this.preBuildStatus = interface_1.PreBuildStatus.DECIDED;
            this.preBuildResult = _yes;
        }
        for (const e of this.modifiedListeners)
            setTimeout(() => e(this.preBuildResult), 0);
    }
    modifiedCheckFinishPromise() {
        if (this.preBuildStatus === interface_1.PreBuildStatus.DECIDED) {
            return Promise.resolve(this.preBuildResult);
        }
        else {
            return new Promise((resolve, reject) => {
                this.modifiedListeners.push(resolve);
            });
        }
    }
    toJson() {
        const o = {
            id: this.id,
            volatile: (this.status !== interface_1.BuildStatus.NOT_STARTED && this.status !== interface_1.BuildStatus.FINISHED) ||
                this.volatile,
            result: this.result,
            dependencies: this.dependencies.map((t) => [...t]),
            revision: this.revision,
        };
        return o;
    }
    fromJson(j) {
        this.volatile = j.volatile;
        this.lastResult = this.result = j.result;
        this.dependencies = j.dependencies;
        this.revision = j.revision || 0;
    }
}
exports.default = ProgressImpl;
