"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDevice = exports.getUsbDevice = exports.getRemoteDevice = exports.getLocalDevice = exports.getDeviceManager = exports.enumerateDevices = exports.injectLibraryBlob = exports.injectLibraryFile = exports.attach = exports.kill = exports.resume = exports.spawn = exports.querySystemParameters = exports.Bus = exports.ChildOrigin = exports.Cancellable = exports.IOStream = exports.EndpointParameters = exports.PortalService = exports.PortalMembership = exports.RelayKind = exports.Relay = exports.LogLevel = exports.MessageType = exports.ScriptRuntime = exports.Script = exports.SessionDetachReason = exports.Session = exports.Realm = exports.Stdio = exports.Scope = exports.DeviceType = exports.Device = exports.DeviceManager = void 0;
const busModule = require("./bus");
const cancellableModule = require("./cancellable");
const childModule = require("./child");
const deviceManagerModule = require("./device_manager");
const deviceModule = require("./device");
const endpointParametersModule = require("./endpoint_parameters");
const iostreamModule = require("./iostream");
const portalMembershipModule = require("./portal_membership");
const portalServiceModule = require("./portal_service");
const relayModule = require("./relay");
const scriptModule = require("./script");
const sessionModule = require("./session");
exports.DeviceManager = deviceManagerModule.DeviceManager;
exports.Device = deviceModule.Device;
exports.DeviceType = deviceModule.DeviceType;
exports.Scope = deviceModule.Scope;
exports.Stdio = deviceModule.Stdio;
exports.Realm = deviceModule.Realm;
exports.Session = sessionModule.Session;
exports.SessionDetachReason = sessionModule.SessionDetachReason;
exports.Script = scriptModule.Script;
exports.ScriptRuntime = scriptModule.ScriptRuntime;
exports.MessageType = scriptModule.MessageType;
exports.LogLevel = scriptModule.LogLevel;
exports.Relay = relayModule.Relay;
exports.RelayKind = relayModule.RelayKind;
exports.PortalMembership = portalMembershipModule.PortalMembership;
exports.PortalService = portalServiceModule.PortalService;
exports.EndpointParameters = endpointParametersModule.EndpointParameters;
exports.IOStream = iostreamModule.IOStream;
exports.Cancellable = cancellableModule.Cancellable;
exports.ChildOrigin = childModule.ChildOrigin;
exports.Bus = busModule.Bus;
let sharedDeviceManager = null;
async function querySystemParameters(cancellable) {
    const device = await getLocalDevice(cancellable);
    return await device.querySystemParameters(cancellable);
}
exports.querySystemParameters = querySystemParameters;
async function spawn(program, options = {}, cancellable) {
    const device = await getLocalDevice(cancellable);
    return await device.spawn(program, options, cancellable);
}
exports.spawn = spawn;
async function resume(target, cancellable) {
    const device = await getLocalDevice(cancellable);
    await device.resume(target, cancellable);
}
exports.resume = resume;
async function kill(target, cancellable) {
    const device = await getLocalDevice(cancellable);
    await device.kill(target, cancellable);
}
exports.kill = kill;
async function attach(target, options = {}, cancellable) {
    const device = await getLocalDevice(cancellable);
    return await device.attach(target, options, cancellable);
}
exports.attach = attach;
async function injectLibraryFile(target, path, entrypoint, data, cancellable) {
    const device = await getLocalDevice(cancellable);
    return await device.injectLibraryFile(target, path, entrypoint, data, cancellable);
}
exports.injectLibraryFile = injectLibraryFile;
async function injectLibraryBlob(target, blob, entrypoint, data, cancellable) {
    const device = await getLocalDevice(cancellable);
    return await device.injectLibraryBlob(target, blob, entrypoint, data, cancellable);
}
exports.injectLibraryBlob = injectLibraryBlob;
async function enumerateDevices(cancellable) {
    const deviceManager = getDeviceManager();
    return await deviceManager.enumerateDevices(cancellable);
}
exports.enumerateDevices = enumerateDevices;
;
function getDeviceManager() {
    if (sharedDeviceManager === null) {
        sharedDeviceManager = new deviceManagerModule.DeviceManager();
    }
    return sharedDeviceManager;
}
exports.getDeviceManager = getDeviceManager;
function getLocalDevice(cancellable) {
    return getMatchingDevice(device => device.type === exports.DeviceType.Local, {}, cancellable);
}
exports.getLocalDevice = getLocalDevice;
function getRemoteDevice(cancellable) {
    return getMatchingDevice(device => device.type === exports.DeviceType.Remote, {}, cancellable);
}
exports.getRemoteDevice = getRemoteDevice;
function getUsbDevice(options, cancellable) {
    return getMatchingDevice(device => device.type === exports.DeviceType.Usb, options, cancellable);
}
exports.getUsbDevice = getUsbDevice;
function getDevice(id, options, cancellable) {
    return getMatchingDevice(device => device.id === id, options, cancellable);
}
exports.getDevice = getDevice;
async function getMatchingDevice(predicate, options = {}, cancellable) {
    const device = await findMatchingDevice(predicate, cancellable);
    if (device !== null) {
        return device;
    }
    const { timeout = 0 } = options;
    if (timeout === 0) {
        throw new Error("Device not found");
    }
    const getDeviceEventually = new Promise((resolve, reject) => {
        const deviceManager = getDeviceManager();
        deviceManager.added.connect(onDeviceAdded);
        const timer = (timeout !== null) ? setTimeout(onTimeout, timeout) : null;
        if (cancellable !== undefined) {
            cancellable.cancelled.connect(onCancel);
            if (cancellable.isCancelled) {
                onCancel();
                return;
            }
        }
        findMatchingDevice(predicate, cancellable)
            .then(device => {
            if (device !== null) {
                onSuccess(device);
            }
        })
            .catch(onError);
        function onDeviceAdded(device) {
            if (predicate(device)) {
                onSuccess(device);
            }
        }
        function onSuccess(device) {
            stopMonitoring();
            resolve(device);
        }
        function onError(error) {
            stopMonitoring();
            reject(error);
        }
        function onTimeout() {
            onError(new Error("Timed out while waiting for device to appear"));
        }
        function onCancel() {
            onError(new Error("Operation was cancelled"));
        }
        function stopMonitoring() {
            if (cancellable !== undefined) {
                cancellable.cancelled.disconnect(onCancel);
            }
            if (timer !== null) {
                clearTimeout(timer);
            }
            deviceManager.added.disconnect(onDeviceAdded);
        }
    });
    return await getDeviceEventually;
}
async function findMatchingDevice(predicate, cancellable) {
    const deviceManager = getDeviceManager();
    const devices = await deviceManager.enumerateDevices(cancellable);
    const matching = devices.filter(predicate);
    if (matching.length === 0) {
        return null;
    }
    return matching[0];
}
