/**
 * Copyright (c) Microsoft Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import * as events from './events';
// -- Reuse boundary -- Everything below this line is reused in the vscode extension.
export class TestServerConnectionClosedError extends Error {
}
export class WebSocketTestServerTransport {
    _ws;
    constructor(url) {
        this._ws = new WebSocket(url);
    }
    onmessage(listener) {
        this._ws.addEventListener('message', event => listener(event.data.toString()));
    }
    onopen(listener) {
        this._ws.addEventListener('open', listener);
    }
    onerror(listener) {
        this._ws.addEventListener('error', listener);
    }
    onclose(listener) {
        this._ws.addEventListener('close', listener);
    }
    send(data) {
        this._ws.send(data);
    }
    close() {
        this._ws.close();
    }
}
export class TestServerConnection {
    onClose;
    onReport;
    onStdio;
    onTestFilesChanged;
    onLoadTraceRequested;
    onTestPaused;
    _onCloseEmitter = new events.EventEmitter();
    _onReportEmitter = new events.EventEmitter();
    _onStdioEmitter = new events.EventEmitter();
    _onTestFilesChangedEmitter = new events.EventEmitter();
    _onLoadTraceRequestedEmitter = new events.EventEmitter();
    _onTestPausedEmitter = new events.EventEmitter();
    _lastId = 0;
    _transport;
    _callbacks = new Map();
    _connectedPromise;
    _isClosed = false;
    constructor(transport) {
        this.onClose = this._onCloseEmitter.event;
        this.onReport = this._onReportEmitter.event;
        this.onStdio = this._onStdioEmitter.event;
        this.onTestFilesChanged = this._onTestFilesChangedEmitter.event;
        this.onLoadTraceRequested = this._onLoadTraceRequestedEmitter.event;
        this.onTestPaused = this._onTestPausedEmitter.event;
        this._transport = transport;
        this._transport.onmessage(data => {
            const message = JSON.parse(data);
            const { id, result, error, method, params } = message;
            if (id) {
                const callback = this._callbacks.get(id);
                if (!callback)
                    return;
                this._callbacks.delete(id);
                if (error)
                    callback.reject(new Error(error));
                else
                    callback.resolve(result);
            }
            else {
                this._dispatchEvent(method, params);
            }
        });
        const pingInterval = setInterval(() => this._sendMessage('ping').catch(() => { }), 30000);
        this._connectedPromise = new Promise((f, r) => {
            this._transport.onopen(f);
            this._transport.onerror(r);
        });
        this._transport.onclose(() => {
            this._isClosed = true;
            this._onCloseEmitter.fire();
            clearInterval(pingInterval);
            for (const callback of this._callbacks.values())
                callback.reject(callback.error);
            this._callbacks.clear();
        });
    }
    isClosed() {
        return this._isClosed;
    }
    async _sendMessage(method, params) {
        const logForTest = globalThis.__logForTest;
        logForTest?.({ method, params });
        await this._connectedPromise;
        const id = ++this._lastId;
        const message = { id, method, params };
        // Capture proper stack trace in the error here.
        const error = new TestServerConnectionClosedError(`${method}: test server connection closed`);
        this._transport.send(JSON.stringify(message));
        return new Promise((resolve, reject) => {
            this._callbacks.set(id, { resolve, reject, error });
        });
    }
    _sendMessageNoReply(method, params) {
        this._sendMessage(method, params).catch(() => { });
    }
    _dispatchEvent(method, params) {
        if (method === 'report')
            this._onReportEmitter.fire(params);
        else if (method === 'stdio')
            this._onStdioEmitter.fire(params);
        else if (method === 'testFilesChanged')
            this._onTestFilesChangedEmitter.fire(params);
        else if (method === 'loadTraceRequested')
            this._onLoadTraceRequestedEmitter.fire(params);
        else if (method === 'testPaused')
            this._onTestPausedEmitter.fire(params);
    }
    async initialize(params) {
        await this._sendMessage('initialize', params);
    }
    async ping(params) {
        await this._sendMessage('ping', params);
    }
    async pingNoReply(params) {
        this._sendMessageNoReply('ping', params);
    }
    async watch(params) {
        await this._sendMessage('watch', params);
    }
    watchNoReply(params) {
        this._sendMessageNoReply('watch', params);
    }
    async open(params) {
        await this._sendMessage('open', params);
    }
    openNoReply(params) {
        this._sendMessageNoReply('open', params);
    }
    async resizeTerminal(params) {
        await this._sendMessage('resizeTerminal', params);
    }
    resizeTerminalNoReply(params) {
        this._sendMessageNoReply('resizeTerminal', params);
    }
    async checkBrowsers(params) {
        return await this._sendMessage('checkBrowsers', params);
    }
    async installBrowsers(params) {
        await this._sendMessage('installBrowsers', params);
    }
    async runGlobalSetup(params) {
        return await this._sendMessage('runGlobalSetup', params);
    }
    async runGlobalTeardown(params) {
        return await this._sendMessage('runGlobalTeardown', params);
    }
    async clearCache(params) {
        return await this._sendMessage('clearCache', params);
    }
    async listFiles(params) {
        return await this._sendMessage('listFiles', params);
    }
    async listTests(params) {
        return await this._sendMessage('listTests', params);
    }
    async runTests(params) {
        return await this._sendMessage('runTests', params);
    }
    async findRelatedTestFiles(params) {
        return await this._sendMessage('findRelatedTestFiles', params);
    }
    async stopTests(params) {
        await this._sendMessage('stopTests', params);
    }
    stopTestsNoReply(params) {
        this._sendMessageNoReply('stopTests', params);
    }
    async closeGracefully(params) {
        await this._sendMessage('closeGracefully', params);
    }
    close() {
        try {
            this._transport.close();
        }
        catch {
        }
    }
}
//# sourceMappingURL=testServerConnection.js.map