import { log } from "@ledgerhq/logs";
import { Observable, from, of, EMPTY, concat, throwError } from "rxjs";
import { concatMap, delay, scan, distinctUntilChanged, throttleTime } from "rxjs/operators";
import { CantOpenDevice, DeviceInOSUExpected } from "@ledgerhq/errors";
import { withDevicePolling, withDevice } from "./deviceAccess";
import getDeviceInfo from "./getDeviceInfo";
import flash from "./flash";
import installFinalFirmware from "./installFinalFirmware";
import { hasFinalFirmware } from "./hasFinalFirmware";
const wait2s = of({
    type: "wait",
}).pipe(delay(2000));
const main = (deviceId, { final, shouldFlashMCU }) => {
    log("hw", "firmwareUpdate-main started");
    const withFinal = hasFinalFirmware(final);
    const withDeviceInfo = withDevicePolling(deviceId)(transport => from(getDeviceInfo(transport)), () => true);
    const withDeviceInstall = install => withDevicePolling(deviceId)(install, e => e instanceof CantOpenDevice);
    const waitForBootloader = withDeviceInfo.pipe(concatMap(deviceInfo => (deviceInfo.isBootloader ? EMPTY : concat(wait2s, waitForBootloader))));
    const potentialAutoFlash = withDeviceInfo.pipe(concatMap(deviceInfo => deviceInfo.isOSU
        ? EMPTY
        : withDevice(deviceId)(transport => new Observable(o => {
            const timeout = setTimeout(() => {
                log("firmware", "potentialAutoFlash timeout");
                o.complete();
            }, 20000);
            const disconnect = () => {
                log("firmware", "potentialAutoFlash disconnect");
                o.complete();
            };
            transport.on("disconnect", disconnect);
            return () => {
                clearTimeout(timeout);
                transport.off("disconnect", disconnect);
            };
        }))));
    const bootloaderLoop = withDeviceInfo.pipe(concatMap(deviceInfo => deviceInfo.isBootloader
        ? concat(withDeviceInstall(flash(final)), wait2s, bootloaderLoop)
        : EMPTY));
    const finalStep = !withFinal
        ? EMPTY
        : withDeviceInfo.pipe(concatMap(deviceInfo => !deviceInfo.isOSU
            ? throwError(() => new DeviceInOSUExpected())
            : withDeviceInstall(installFinalFirmware)));
    const all = shouldFlashMCU
        ? concat(waitForBootloader, bootloaderLoop, finalStep)
        : concat(potentialAutoFlash, finalStep);
    return all.pipe(scan((acc, e) => {
        if (e.type === "install") {
            return {
                installing: e.step,
                progress: 0,
            };
        }
        if (e.type === "bulk-progress") {
            return { ...acc, progress: e.progress };
        }
        return acc;
    }, {
        progress: 0,
        installing: null,
    }), distinctUntilChanged(), throttleTime(100));
};
export default main;
//# sourceMappingURL=firmwareUpdate-main.js.map