"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExtensionHostManager = void 0;
const tslib_1 = require("tslib");
const assert_1 = tslib_1.__importDefault(require("assert"));
const child_process_1 = tslib_1.__importDefault(require("child_process"));
const is_running_1 = tslib_1.__importDefault(require("is-running"));
const tree_kill_1 = tslib_1.__importDefault(require("tree-kill"));
const di_1 = require("@opensumi/di");
const ide_core_common_1 = require("@opensumi/ide-core-common");
const port_1 = require("@opensumi/ide-core-common/lib/node/port");
const common_1 = require("../common");
let ExtensionHostManager = class ExtensionHostManager {
    constructor() {
        this.processMap = new Map();
        this.init();
    }
    init() {
        // Debug Auto Launch 插件 https://github.com/microsoft/vscode/blob/1.44.2/extensions/debug-auto-launch/src/extension.ts#L120
        // 依赖了 VSCODE_PID 来查找子进程
        process.env['VSCODE_PID'] = String(process.pid);
    }
    fork(modulePath, ...args) {
        const extProcess = child_process_1.default.fork(modulePath, ...args);
        (0, assert_1.default)(extProcess.pid, `fork ${modulePath} error`);
        this.processMap.set(extProcess.pid, extProcess);
        return extProcess.pid;
    }
    send(pid, message) {
        return new Promise((resolve, reject) => {
            const extProcess = this.processMap.get(pid);
            if (!extProcess) {
                reject(new Error(`Can't find process with pid ${pid}`));
                return;
            }
            extProcess.send(message, (err) => {
                if (err) {
                    reject(err);
                }
                else {
                    resolve();
                }
            });
        });
    }
    isRunning(pid) {
        return (0, is_running_1.default)(pid);
    }
    treeKill(pid) {
        return new Promise((resolve, reject) => {
            (0, tree_kill_1.default)(pid, (err) => {
                if (err) {
                    reject(err);
                }
                else {
                    resolve();
                }
            });
        });
    }
    kill(pid, signal) {
        const extProcess = this.processMap.get(pid);
        extProcess === null || extProcess === void 0 ? void 0 : extProcess.kill(signal);
    }
    isKilled(pid) {
        const extProcess = this.processMap.get(pid);
        if (!extProcess) {
            return true;
        }
        return extProcess.killed;
    }
    findDebugPort(startPort, giveUpAfter, timeout) {
        return (0, port_1.findFreePort)(startPort, giveUpAfter, timeout);
    }
    onOutput(pid, listener) {
        const extProcess = this.processMap.get(pid);
        if (!extProcess) {
            return;
        }
        (0, assert_1.default)(extProcess.stdout, 'ext process spawn failed');
        (0, assert_1.default)(extProcess.stderr, 'ext process spawn failed');
        extProcess.stdout.setEncoding('utf8');
        extProcess.stderr.setEncoding('utf8');
        const onStdout = ide_core_common_1.Event.fromNodeEventEmitter(extProcess.stdout, 'data');
        const onStderr = ide_core_common_1.Event.fromNodeEventEmitter(extProcess.stderr, 'data');
        const onOutput = ide_core_common_1.Event.any(ide_core_common_1.Event.map(onStdout, (o) => ({ type: common_1.OutputType.STDOUT, data: `%c${o}`, format: [''] })), ide_core_common_1.Event.map(onStderr, (o) => ({ type: common_1.OutputType.STDERR, data: `%c${o}`, format: ['color: red'] })));
        // Debounce all output, so we can render it in the Chrome console as a group
        const onDebouncedOutput = ide_core_common_1.Event.debounce(onOutput, (r, o) => r
            ? {
                data: r.data + o.data,
                format: [...r.format, ...o.format],
                type: [r.type, o.type].includes(common_1.OutputType.STDERR) ? common_1.OutputType.STDERR : common_1.OutputType.STDOUT,
            }
            : { data: o.data, format: o.format, type: o.type }, 100);
        onDebouncedOutput(listener);
    }
    onExit(pid, listener) {
        const extProcess = this.processMap.get(pid);
        if (!extProcess) {
            return;
        }
        extProcess.once('exit', listener);
    }
    onMessage(pid, listener) {
        const extProcess = this.processMap.get(pid);
        if (!extProcess) {
            return;
        }
        extProcess.on('message', listener);
    }
    disposeProcess(pid) {
        const extProcess = this.processMap.get(pid);
        if (extProcess) {
            if (this.isRunning(pid)) {
                extProcess.kill();
            }
            this.processMap.delete(pid);
        }
    }
    async dispose() {
        await Promise.all([...this.processMap.keys()].map(async (pid) => {
            const isRunning = await this.isRunning(pid);
            if (isRunning) {
                await this.treeKill(pid);
            }
        }));
        this.processMap.clear();
    }
};
exports.ExtensionHostManager = ExtensionHostManager;
exports.ExtensionHostManager = ExtensionHostManager = tslib_1.__decorate([
    (0, di_1.Injectable)(),
    tslib_1.__metadata("design:paramtypes", [])
], ExtensionHostManager);
//# sourceMappingURL=extension.host.manager.js.map