"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Realm = exports.Stdio = exports.Scope = exports.DeviceType = exports.Device = void 0;
const bus_1 = require("./bus");
const iostream_1 = require("./iostream");
const session_1 = require("./session");
const signals_1 = require("./signals");
const minimatch_1 = require("minimatch");
const util_1 = require("util");
class Device {
    constructor(impl) {
        this.impl = impl;
        this.bus = new bus_1.Bus(impl.bus);
        const { signals } = impl;
        this.spawnAdded = new signals_1.Signal(signals, "spawn-added");
        this.spawnRemoved = new signals_1.Signal(signals, "spawn-removed");
        this.childAdded = new signals_1.Signal(signals, "child-added");
        this.childRemoved = new signals_1.Signal(signals, "child-removed");
        this.processCrashed = new signals_1.Signal(signals, "process-crashed");
        this.output = new signals_1.Signal(signals, "output");
        this.uninjected = new signals_1.Signal(signals, "uninjected");
        this.lost = new signals_1.Signal(signals, "lost");
    }
    get id() {
        return this.impl.id;
    }
    get name() {
        return this.impl.name;
    }
    get icon() {
        return this.impl.icon;
    }
    get type() {
        return this.impl.type;
    }
    get isLost() {
        return this.impl.isLost;
    }
    querySystemParameters(cancellable) {
        return this.impl.querySystemParameters(cancellable);
    }
    getFrontmostApplication(options = {}, cancellable) {
        const { scope = null, } = options;
        return this.impl.getFrontmostApplication(scope, cancellable);
    }
    enumerateApplications(options = {}, cancellable) {
        const { identifiers = [], scope = null, } = options;
        return this.impl.enumerateApplications(identifiers, scope, cancellable);
    }
    enumerateProcesses(options = {}, cancellable) {
        const { pids = [], scope = null, } = options;
        return this.impl.enumerateProcesses(pids, scope, cancellable);
    }
    async getProcess(name, options = {}, cancellable) {
        const { scope = Scope.Minimal, } = options;
        const processes = await this.enumerateProcesses({ scope }, cancellable);
        const mm = new minimatch_1.Minimatch(name.toLowerCase());
        const matching = processes.filter(process => mm.match(process.name.toLowerCase()));
        if (matching.length === 1) {
            return matching[0];
        }
        else if (matching.length > 1) {
            throw new Error("Ambiguous name; it matches: " + matching.map(process => `${process.name} (pid: ${process.pid})`).join(", "));
        }
        else {
            throw new Error("Process not found");
        }
    }
    enableSpawnGating(cancellable) {
        return this.impl.enableSpawnGating(cancellable);
    }
    disableSpawnGating(cancellable) {
        return this.impl.disableSpawnGating(cancellable);
    }
    enumeratePendingSpawn(cancellable) {
        return this.impl.enumeratePendingSpawn(cancellable);
    }
    enumeratePendingChildren(cancellable) {
        return this.impl.enumeratePendingChildren(cancellable);
    }
    spawn(program, options = {}, cancellable) {
        const pendingOptions = Object.assign({}, options);
        let argv = consumeOption("argv");
        if (typeof program !== "string") {
            argv = program;
            program = argv[0];
            if (argv.length === 1) {
                argv = null;
            }
        }
        const envp = consumeOption("envp");
        const env = consumeOption("env");
        const cwd = consumeOption("cwd");
        const stdio = consumeOption("stdio");
        const aux = pendingOptions;
        return this.impl.spawn(program, argv, envp, env, cwd, stdio, aux, cancellable);
        function consumeOption(name) {
            const value = pendingOptions[name];
            if (value === undefined) {
                return null;
            }
            delete pendingOptions[name];
            return value;
        }
    }
    async input(target, data, cancellable) {
        const pid = await this.getPid(target, cancellable);
        return this.impl.input(pid, data, cancellable);
    }
    async resume(target, cancellable) {
        const pid = await this.getPid(target, cancellable);
        return this.impl.resume(pid, cancellable);
    }
    async kill(target, cancellable) {
        const pid = await this.getPid(target, cancellable);
        return this.impl.kill(pid, cancellable);
    }
    async attach(target, options = {}, cancellable) {
        const { realm = null, persistTimeout = null, } = options;
        const pid = await this.getPid(target, cancellable);
        return new session_1.Session(await this.impl.attach(pid, realm, persistTimeout, cancellable));
    }
    async injectLibraryFile(target, path, entrypoint, data, cancellable) {
        const pid = await this.getPid(target, cancellable);
        return this.impl.injectLibraryFile(pid, path, entrypoint, data, cancellable);
    }
    async injectLibraryBlob(target, blob, entrypoint, data, cancellable) {
        const pid = await this.getPid(target, cancellable);
        return this.impl.injectLibraryBlob(pid, blob, entrypoint, data, cancellable);
    }
    async openChannel(address, cancellable) {
        return new iostream_1.IOStream(await this.impl.openChannel(address, cancellable));
    }
    async getPid(target, cancellable) {
        if (typeof target === "number") {
            return target;
        }
        const process = await this.getProcess(target, {}, cancellable);
        return process.pid;
    }
    [util_1.inspect.custom](depth, options) {
        return "Device " + (0, util_1.inspect)({
            id: this.id,
            name: this.name,
            icon: this.icon,
            type: this.type
        }, options);
    }
}
exports.Device = Device;
var DeviceType;
(function (DeviceType) {
    DeviceType["Local"] = "local";
    DeviceType["Remote"] = "remote";
    DeviceType["Usb"] = "usb";
})(DeviceType = exports.DeviceType || (exports.DeviceType = {}));
/**
 * How much data to collect about a given application or process.
 */
var Scope;
(function (Scope) {
    /**
     * Don't collect any parameters. This is the default.
     */
    Scope["Minimal"] = "minimal";
    /**
     * Collect all parameters except icons.
     */
    Scope["Metadata"] = "metadata";
    /**
     * Collect all parameters, including icons.
     */
    Scope["Full"] = "full";
})(Scope = exports.Scope || (exports.Scope = {}));
var Stdio;
(function (Stdio) {
    Stdio["Inherit"] = "inherit";
    Stdio["Pipe"] = "pipe";
})(Stdio = exports.Stdio || (exports.Stdio = {}));
var Realm;
(function (Realm) {
    Realm["Native"] = "native";
    Realm["Emulated"] = "emulated";
})(Realm = exports.Realm || (exports.Realm = {}));
