"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const rxjs_1 = require("rxjs");
const getDeviceInfo_1 = __importDefault(require("../hw/getDeviceInfo"));
const listAppsUseCase_1 = require("../device/use-cases/listAppsUseCase");
const execWithTransport_1 = require("../device/use-cases/execWithTransport");
const logic_1 = require("./logic");
const runner_1 = require("./runner");
const operators_1 = require("rxjs/operators");
const logs_1 = require("@ledgerhq/logs");
/**
 * Tries to install a list of apps
 *
 * @param transport Transport instance
 * @param appNames List of app names to install
 * @param onSuccessObs Optional observable to run after the installation
 * @param allowPartialDependencies If true, keep installing apps even if some are missing
 * @returns Observable of InlineAppInstallEvent or ConnectAppEvent
 * - Event "inline-install" contains a global progress of the installation
 */
const inlineAppInstall = ({ transport, appNames, onSuccessObs, allowPartialDependencies = false, }) => {
    const tracer = new logs_1.LocalTracer("hw", {
        ...transport.getTraceContext(),
        function: "inlineAppInstall",
    });
    tracer.trace("Starting inline app install");
    return (0, rxjs_1.concat)((0, rxjs_1.of)({
        type: "listing-apps",
    }), (0, rxjs_1.from)((0, getDeviceInfo_1.default)(transport)).pipe((0, operators_1.mergeMap)(deviceInfo => {
        tracer.trace("Got device info", { deviceInfo });
        return (0, listAppsUseCase_1.listAppsUseCase)(transport, deviceInfo);
    }), (0, operators_1.mergeMap)(e => {
        // Bubble up events
        if (e.type === "device-permission-granted" || e.type === "device-permission-requested") {
            return (0, rxjs_1.of)(e);
        }
        if (e.type === "result") {
            // Figure out the operations that need to happen
            const state = appNames.reduce((state, name) => (0, logic_1.reducer)(state, {
                type: "install",
                name,
                allowPartialDependencies,
            }), (0, logic_1.initState)(e.result));
            // Failed appOps in this flow will throw by default but if we're here
            // it means we didn't throw, so we wan't to notify the action about it.
            const maybeSkippedEvent = state.skippedAppOps.length
                ? (0, rxjs_1.of)({
                    type: "some-apps-skipped",
                    skippedAppOps: state.skippedAppOps,
                })
                : rxjs_1.EMPTY;
            if (!state.installQueue.length) {
                // There's nothing to install, we can consider this a success.
                return (0, rxjs_1.defer)(onSuccessObs || (() => rxjs_1.EMPTY));
            }
            if ((0, logic_1.isOutOfMemoryState)((0, logic_1.predictOptimisticState)(state))) {
                // In this case we can't install either by lack of storage, or permissions,
                // we fallback to the error case listing the missing apps.
                const missingAppNames = state.installQueue;
                tracer.trace("Out of memory", {
                    appNames: missingAppNames,
                    appName: missingAppNames[0] || appNames[0],
                });
                return (0, rxjs_1.of)({
                    type: "app-not-installed",
                    appNames: missingAppNames,
                    appName: missingAppNames[0] || appNames[0], // TODO remove when LLD/LLM integrate appNames
                });
            }
            const exec = (0, execWithTransport_1.execWithTransport)(transport);
            return (0, rxjs_1.concat)((0, rxjs_1.of)({
                type: "listed-apps",
                installQueue: state.installQueue,
            }), maybeSkippedEvent, (0, runner_1.runAllWithProgress)(state, exec).pipe((0, operators_1.throttleTime)(100), (0, operators_1.map)(({ globalProgress, itemProgress, installQueue, currentAppOp }) => ({
                type: "inline-install",
                progress: globalProgress,
                itemProgress,
                installQueue,
                currentAppOp,
            }))), (0, rxjs_1.defer)(onSuccessObs || (() => rxjs_1.EMPTY)));
        }
        return rxjs_1.EMPTY;
    })));
};
exports.default = inlineAppInstall;
//# sourceMappingURL=inlineAppInstall.js.map