"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.stopScanning = exports.startScanning = void 0;
exports.hasAvailableDrives = hasAvailableDrives;
const drive_scanner_1 = require("./drive-scanner");
const sdk = __importStar(require("etcher-sdk"));
const outdent_1 = __importDefault(require("outdent"));
const lodash_1 = require("lodash");
const api_1 = require("./api");
let availableDrives = [];
function hasAvailableDrives() {
    return availableDrives.length > 0;
}
drive_scanner_1.scanner.on('error', (error) => {
    // Stop the drive scanning loop in case of errors,
    // otherwise we risk presenting the same error over
    // and over again to the user, while also heavily
    // spamming our error reporting service.
    drive_scanner_1.scanner.stop();
    console.log('scanner error', error);
});
function setDrives(drives) {
    availableDrives = (0, lodash_1.values)(drives);
    (0, api_1.emitDrives)(drives);
}
function getDrives() {
    return (0, lodash_1.keyBy)(availableDrives, 'device');
}
async function addDrive(drive) {
    const preparedDrive = prepareDrive(drive);
    if (!(await driveIsAllowed(preparedDrive))) {
        return;
    }
    const drives = getDrives();
    drives[preparedDrive.device] = preparedDrive;
    setDrives(drives);
}
function removeDrive(drive) {
    const preparedDrive = prepareDrive(drive);
    const drives = getDrives();
    delete drives[preparedDrive.device];
    setDrives(drives);
}
async function driveIsAllowed(drive) {
    // const driveBlacklist = (await settings.get("driveBlacklist")) || [];
    const driveBlacklist = [];
    return !(driveBlacklist.includes(drive.devicePath) ||
        driveBlacklist.includes(drive.device) ||
        driveBlacklist.includes(drive.raw));
}
function prepareDrive(drive) {
    if (drive instanceof sdk.sourceDestination.BlockDevice) {
        // @ts-ignore (BlockDevice.drive is private)
        return drive.drive;
    }
    else if (drive instanceof sdk.sourceDestination.UsbbootDrive) {
        // This is a workaround etcher expecting a device string and a size
        // @ts-ignore
        drive.device = drive.usbDevice.portId;
        drive.size = null;
        // @ts-ignore
        drive.progress = 0;
        drive.disabled = true;
        drive.on('progress', (progress) => {
            updateDriveProgress(drive, progress);
        });
        return drive;
    }
    else if (drive instanceof sdk.sourceDestination.DriverlessDevice) {
        const description = COMPUTE_MODULE_DESCRIPTIONS[drive.deviceDescriptor.idProduct.toString()] || 'Compute Module';
        return {
            device: `${usbIdToString(drive.deviceDescriptor.idVendor)}:${usbIdToString(drive.deviceDescriptor.idProduct)}`,
            displayName: 'Missing drivers',
            description,
            mountpoints: [],
            isReadOnly: false,
            isSystem: false,
            disabled: true,
            icon: 'warning',
            size: null,
            link: 'https://www.raspberrypi.com/documentation/computers/compute-module.html#flashing-the-compute-module-emmc',
            linkCTA: 'Install',
            linkTitle: 'Install missing drivers',
            linkMessage: (0, outdent_1.default) `
				Would you like to download the necessary drivers from the Raspberry Pi Foundation?
				This will open your browser.


				Once opened, download and run the installer from the "Windows Installer" section to install the drivers
			`,
        };
    }
}
/**
 * @summary The radix used by USB ID numbers
 */
const USB_ID_RADIX = 16;
/**
 * @summary The expected length of a USB ID number
 */
const USB_ID_LENGTH = 4;
/**
 * @summary Convert a USB id (e.g. product/vendor) to a string
 *
 * @example
 * console.log(usbIdToString(2652))
 * > '0x0a5c'
 */
function usbIdToString(id) {
    return `0x${(0, lodash_1.padStart)(id.toString(USB_ID_RADIX), USB_ID_LENGTH, '0')}`;
}
function updateDriveProgress(drive, progress) {
    const drives = getDrives();
    // @ts-ignore
    const driveInMap = drives[drive.device];
    if (driveInMap) {
        // @ts-ignore
        drives[drive.device] = { ...driveInMap, progress };
        setDrives(drives);
    }
}
/**
 * @summary Product ID of BCM2708
 */
const USB_PRODUCT_ID_BCM2708_BOOT = 0x2763;
/**
 * @summary Product ID of BCM2710
 */
const USB_PRODUCT_ID_BCM2710_BOOT = 0x2764;
/**
 * @summary Compute module descriptions
 */
const COMPUTE_MODULE_DESCRIPTIONS = {
    [USB_PRODUCT_ID_BCM2708_BOOT]: 'Compute Module 1',
    [USB_PRODUCT_ID_BCM2710_BOOT]: 'Compute Module 3',
};
const startScanning = () => {
    drive_scanner_1.scanner.on('attach', (drive) => addDrive(drive));
    drive_scanner_1.scanner.on('detach', (drive) => removeDrive(drive));
    drive_scanner_1.scanner.start();
};
exports.startScanning = startScanning;
const stopScanning = () => {
    drive_scanner_1.scanner.stop();
};
exports.stopScanning = stopScanning;
