"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.startDebugAdapter = startDebugAdapter;
exports.connectDebugAdapter = connectDebugAdapter;
exports.directDebugAdapter = directDebugAdapter;
exports.namedPipeDebugAdapter = namedPipeDebugAdapter;
const tslib_1 = require("tslib");
const assert_1 = tslib_1.__importDefault(require("assert"));
const child_process_1 = require("child_process");
const events_1 = require("events");
const net_1 = tslib_1.__importDefault(require("net"));
const stream_1 = tslib_1.__importDefault(require("stream"));
const abstract_debug_adapter_session_1 = require("./abstract-debug-adapter-session");
/**
 * 启动调试适配器进程
 */
function startDebugAdapter(executable, cp) {
    const options = { stdio: ['pipe', 'pipe', 2] };
    if (executable.options) {
        options.cwd = executable.options.cwd;
        options.env = Object.assign({}, process.env);
        Object.assign(options.env, executable.options.env);
    }
    let env = {
        ...process.env,
    };
    if (options.env) {
        env = {
            ...env,
            ...options.env,
        };
    }
    let childProcess;
    if ('command' in executable) {
        const { command, args } = executable;
        const spawnOptions = {
            env,
        };
        if (options.cwd) {
            spawnOptions.cwd = options.cwd;
        }
        childProcess = cp ? cp.spawn(command, args, spawnOptions) : (0, child_process_1.spawn)(command, args, spawnOptions);
    }
    else if ('modulePath' in executable) {
        const forkExecutable = executable;
        const { modulePath, args } = forkExecutable;
        options.stdio.push('ipc');
        childProcess = (0, child_process_1.fork)(modulePath, args, options);
    }
    else {
        throw new Error(`It is not possible to launch debug adapter with the command: ${JSON.stringify(executable)}`);
    }
    (0, assert_1.default)(childProcess.stdin, 'child process spawn failed');
    (0, assert_1.default)(childProcess.stdout, 'child process spawn failed');
    return {
        input: childProcess.stdin,
        output: childProcess.stdout,
        dispose: () => childProcess.kill(),
    };
}
/**
 * 链接远程调试服务
 */
function connectDebugAdapter(server) {
    const socket = net_1.default.createConnection({
        port: server.port,
        host: server.host || '127.0.0.1',
    });
    return {
        input: socket,
        output: socket,
        dispose: () => socket.end(),
    };
}
/**
 * Custom MessageReader to parse DAP messages from the input stream
 */
class MessageReader extends events_1.EventEmitter {
    constructor(input) {
        super();
        this.input = input;
        this.contentLength = -1;
        this.buffer = Buffer.alloc(0);
        input.on('data', this.onData.bind(this));
    }
    onData(data) {
        this.buffer = Buffer.concat([this.buffer, data]);
        while (true) {
            if (this.contentLength === -1) {
                const headerEnd = this.buffer.indexOf('\r\n\r\n');
                if (headerEnd === -1) {
                    break; // Not enough data
                }
                const header = this.buffer.slice(0, headerEnd).toString();
                const match = header.match(/Content-Length: (\d+)/);
                if (match) {
                    this.contentLength = parseInt(match[1], 10);
                    this.buffer = this.buffer.slice(headerEnd + 4);
                }
                else {
                    this.emit('error', new Error('Invalid header'));
                    return;
                }
            }
            if (this.buffer.length >= this.contentLength) {
                const messageBytes = this.buffer.slice(0, this.contentLength);
                this.buffer = this.buffer.slice(this.contentLength);
                this.contentLength = -1;
                const message = JSON.parse(messageBytes.toString());
                this.emit('message', message);
            }
            else {
                break; // Not enough data
            }
        }
    }
}
/**
 * Custom MessageWriter to serialize DAP messages to the output stream
 */
class MessageWriter {
    constructor(output) {
        this.output = output;
    }
    write(message) {
        const json = message;
        const contentLength = Buffer.byteLength(message, 'utf8');
        const header = `Content-Length: ${contentLength}\r\n\r\n`;
        this.output.write(header + json);
    }
}
/**
 * 直接调用插件自己实现的调试适配器
 * 这里通过 server 服务来拉起适配器
 * Modify directDebugAdapter to directly interface with DebugStreamConnection
 */
function directDebugAdapter(id, da) {
    const input = new stream_1.default.PassThrough();
    const output = new stream_1.default.PassThrough();
    const adapter = new abstract_debug_adapter_session_1.DirectDebugAdapter(id, da);
    adapter.start();
    const reader = new MessageReader(input);
    const writer = new MessageWriter(output);
    // Pass messages from the input stream to the adapter
    reader.on('message', (message) => {
        adapter.sendMessage(message);
    });
    // Send messages from the adapter to the output stream
    adapter.onMessageReceived((message) => {
        writer.write(message);
    });
    return {
        input,
        output,
        dispose: () => {
            input.destroy();
            output.destroy();
            adapter.dispose();
        },
    };
}
/**
 * 通过 NamedPipe(在 Windows) 或者 UNIX Domain Socket(IPC socket)(非 Windows) 连接到适配器的实现
 */
function namedPipeDebugAdapter(server) {
    const socket = net_1.default.createConnection(server.path);
    return {
        input: socket,
        output: socket,
        dispose: () => socket.end(),
    };
}
//# sourceMappingURL=extension-debug-adapter-starter.js.map