"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StreamDebugAdapter = exports.DirectDebugAdapter = exports.AbstractDebugAdapter = void 0;
const ide_core_common_1 = require("@opensumi/ide-core-common");
const ide_debug_1 = require("@opensumi/ide-debug");
class AbstractDebugAdapter {
    constructor(id) {
        this.id = id;
    }
}
exports.AbstractDebugAdapter = AbstractDebugAdapter;
class DirectDebugAdapter extends AbstractDebugAdapter {
    constructor(id, implementation) {
        super(id);
        this.id = id;
        this.implementation = implementation;
        this.messageReceivedEmitter = new ide_core_common_1.Emitter();
        this.closeEmitter = new ide_core_common_1.Emitter();
        this.onError = ide_core_common_1.Event.None;
        this.implementation.onDidSendMessage((msg) => {
            this.messageReceivedEmitter.fire(JSON.stringify(msg));
        });
    }
    get onClose() {
        return this.closeEmitter.event;
    }
    get onMessageReceived() {
        return this.messageReceivedEmitter.event;
    }
    async start() { }
    sendMessage(message) {
        this.implementation.handleMessage(message);
    }
    async stop() {
        this.implementation.dispose();
    }
}
exports.DirectDebugAdapter = DirectDebugAdapter;
/**
 * communicate with debug adapter via stream
 */
class StreamDebugAdapter extends AbstractDebugAdapter {
    constructor(id, debugStreamConnection) {
        super(id);
        this.id = id;
        this.debugStreamConnection = debugStreamConnection;
        this.toDispose = new ide_core_common_1.DisposableCollection();
        this.contentLength = -1;
        this.buffer = Buffer.alloc(0);
        this.toDispose.pushAll([
            this.debugStreamConnection,
            ide_core_common_1.Disposable.create(() => this.write(JSON.stringify({ seq: (0, ide_debug_1.getSequenceId)(), type: 'request', command: 'disconnect' }))),
            ide_core_common_1.Disposable.create(() => this.write(JSON.stringify({ seq: (0, ide_debug_1.getSequenceId)(), type: 'request', command: 'terminate' }))),
        ]);
    }
    async start(connection) {
        if (this.frontendConnection) {
            throw new Error('The session has already been started, id: ' + this.id);
        }
        this.frontendConnection = connection;
        this.frontendConnection.onMessage((message) => this.write(message));
        this.frontendConnection.onceClose(() => (this.frontendConnection = undefined));
        this.debugStreamConnection.output.on('data', (data) => this.handleData(data));
        this.debugStreamConnection.output.on('close', () => this.onDebugAdapterExit(1, undefined));
        this.debugStreamConnection.output.on('error', (error) => this.onDebugAdapterError(error));
        this.debugStreamConnection.input.on('error', (error) => this.onDebugAdapterError(error));
    }
    onDebugAdapterExit(exitCode, signal) {
        const event = {
            type: 'event',
            event: 'exited',
            seq: (0, ide_debug_1.getSequenceId)(),
            body: {
                exitCode,
            },
        };
        this.sendToFrontend(JSON.stringify(event));
    }
    onDebugAdapterError(error) {
        const event = {
            type: 'event',
            event: 'error',
            seq: (0, ide_debug_1.getSequenceId)(),
            body: error,
        };
        this.sendToFrontend(JSON.stringify(event));
    }
    handleData(data) {
        this.buffer = Buffer.concat([this.buffer, data]);
        while (true) {
            if (this.contentLength >= 0) {
                if (this.buffer.length >= this.contentLength) {
                    const message = this.buffer.toString('utf8', 0, this.contentLength);
                    this.buffer = this.buffer.slice(this.contentLength);
                    this.contentLength = -1;
                    if (message.length > 0) {
                        this.sendToFrontend(message);
                    }
                    continue;
                }
            }
            else {
                let idx = this.buffer.indexOf(StreamDebugAdapter.CONTENT_LENGTH);
                if (idx > 0) {
                    this.buffer.slice(0, idx);
                    this.buffer = this.buffer.slice(idx);
                }
                idx = this.buffer.indexOf(StreamDebugAdapter.TWO_CRLF);
                if (idx !== -1) {
                    const header = this.buffer.toString('utf8', 0, idx);
                    const lines = header.split(StreamDebugAdapter.HEADER_LINE_SEPARATOR);
                    for (const line of lines) {
                        const pair = line.split(StreamDebugAdapter.HEADER_FIELD_SEPARATOR);
                        if (pair[0] === StreamDebugAdapter.CONTENT_LENGTH) {
                            this.contentLength = +pair[1];
                        }
                    }
                    this.buffer = this.buffer.slice(idx + StreamDebugAdapter.TWO_CRLF.length);
                    continue;
                }
            }
            break;
        }
    }
    sendToFrontend(message) {
        if (this.frontendConnection) {
            this.frontendConnection.send(message);
        }
    }
    write(message) {
        // In the custom bash mode, it is necessary to use \r\n to ensure that it is written in.
        const finalMessage = message + StreamDebugAdapter.CRLF;
        // No more messages will be sent after the Stream is closed.
        if (this.debugStreamConnection.input.writable) {
            this.debugStreamConnection.input.write(`Content-Length: ${Buffer.byteLength(finalMessage, 'utf8')}${StreamDebugAdapter.TWO_CRLF}${finalMessage}`, 'utf8');
        }
    }
    async stop() {
        this.toDispose.dispose();
    }
}
exports.StreamDebugAdapter = StreamDebugAdapter;
StreamDebugAdapter.TWO_CRLF = '\r\n\r\n';
StreamDebugAdapter.CRLF = '\r\n';
StreamDebugAdapter.CONTENT_LENGTH = 'Content-Length';
// allow for non-RFC 2822 conforming line separators
StreamDebugAdapter.HEADER_LINE_SEPARATOR = /\r?\n/;
StreamDebugAdapter.HEADER_FIELD_SEPARATOR = /: +/;
//# sourceMappingURL=abstract-debug-adapter-session.js.map