"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.runOne = exports.runOneAppOp = exports.runAll = exports.runAllWithProgress = exports.runAppOp = void 0;
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const logic_1 = require("./logic");
const promise_1 = require("../promise");
const live_env_1 = require("@ledgerhq/live-env");
const runAppOp = ({ state, appOp, exec, }) => {
    const { appByName, deviceInfo, deviceModel } = state;
    const app = appByName[appOp.name];
    if (!app) {
        const events = [
            {
                type: "runStart",
                appOp,
            },
            {
                type: "runSuccess",
                appOp,
            },
        ];
        // app not in list, we skip it.
        return (0, rxjs_1.from)(events);
    }
    return (0, rxjs_1.concat)((0, rxjs_1.of)({
        type: "runStart",
        appOp,
    }), // we need to allow a 1s delay for the action to be achieved without glitch (bug in old firmware when you do things too closely)
    (0, rxjs_1.defer)(() => (0, promise_1.delay)((0, live_env_1.getEnv)("MANAGER_INSTALL_DELAY"))).pipe((0, operators_1.ignoreElements)()), (0, rxjs_1.defer)(() => exec({
        appOp,
        targetId: deviceInfo.targetId,
        app,
        modelId: deviceModel.id,
        ...(state.skipAppDataBackup ? { skipAppDataBackup: true } : {}),
    })).pipe((0, operators_1.throttleTime)(100), (0, operators_1.materialize)(), (0, operators_1.map)(n => {
        switch (n.kind) {
            case "N":
                return {
                    type: "runProgress",
                    appOp,
                    progress: n?.value?.progress ?? 0,
                };
            case "E":
                return {
                    type: "runError",
                    appOp,
                    error: n.error,
                };
            case "C":
                return {
                    type: "runSuccess",
                    appOp,
                };
        }
    })));
};
exports.runAppOp = runAppOp;
const runAllWithProgress = (state, exec, precision = 100) => {
    const total = state.uninstallQueue.length + state.installQueue.length;
    function globalProgress(s, localProgress) {
        let p = 1 - (s.uninstallQueue.length + s.installQueue.length - localProgress) / total;
        p = Math.round(p * precision) / precision;
        return p;
    }
    return (0, rxjs_1.concat)(...(0, logic_1.getActionPlan)(state).map(appOp => (0, exports.runAppOp)({ state, appOp, exec }))).pipe((0, operators_1.map)(event => {
        if (event.type === "runError") {
            throw event.error;
        }
        return {
            type: "onRunnerEvent",
            event,
        };
    }), (0, operators_1.scan)(logic_1.reducer, state), (0, operators_1.mergeMap)(s => {
        // Nb if you also want to expose the uninstall queue, feel free.
        const { currentProgressSubject, currentAppOp, installQueue } = s;
        if (!currentProgressSubject)
            return (0, rxjs_1.of)({
                globalProgress: globalProgress(s, 0),
                itemProgress: 0,
                installQueue,
                currentAppOp,
            });
        // Expose more information about what's happening during the install
        return currentProgressSubject.pipe((0, operators_1.map)(v => ({
            globalProgress: globalProgress(s, v),
            itemProgress: v,
            installQueue,
            currentAppOp,
        })));
    }), (0, operators_1.distinctUntilChanged)());
};
exports.runAllWithProgress = runAllWithProgress;
// use for CLI, no change of the state over time
const runAll = (state, exec) => (0, rxjs_1.concat)(...(0, logic_1.getActionPlan)(state).map(appOp => (0, exports.runAppOp)({ state, appOp, exec }))).pipe((0, operators_1.map)(event => ({
    type: "onRunnerEvent",
    event,
})), (0, operators_1.reduce)(logic_1.reducer, state));
exports.runAll = runAll;
const runOneAppOp = ({ state, appOp, exec, }) => (0, exports.runAppOp)({ state, appOp, exec }).pipe((0, operators_1.map)(event => ({
    type: "onRunnerEvent",
    event,
})), (0, operators_1.reduce)(logic_1.reducer, state));
exports.runOneAppOp = runOneAppOp;
const runOne = ({ state, exec }) => {
    const next = (0, logic_1.getNextAppOp)(state);
    if (!next)
        return (0, rxjs_1.of)(state);
    return (0, exports.runOneAppOp)({ state, appOp: next, exec });
};
exports.runOne = runOne;
//# sourceMappingURL=runner.js.map