const REQUEST_RESPONSE_TIMEOUT = 10_000;
export class FoxgloveWebSocketAdapter extends EventTarget {
    #worker;
    #connectionClosed = false;
    binaryType = "arraybuffer";
    protocol = "";
    url;
    extensions = "";
    bufferedAmount = 0;
    readyState = WebSocket.CLOSED;
    onerror = ReactNull;
    onopen = ReactNull;
    onclose = ReactNull;
    onmessage = ReactNull;
    /** The connection is not yet open. */
    CONNECTING = WebSocket.CONNECTING;
    /**  The connection is open and ready to communicate. */
    OPEN = WebSocket.OPEN;
    /**  The connection is in the process of closing. */
    CLOSING = WebSocket.CLOSING;
    /**  The connection is closed or couldn't be opened. */
    CLOSED = WebSocket.CLOSED;
    #serviceCallId = 0;
    #eventData = {
        channels: null,
        services: null
    };
    #sendToWorker(message, transferable = []) {
        this.#worker.postMessage(message, transferable);
    }
    #handleMessageEvents() {
        const onWorkerError = (error) => { console.error(`WebWorker error:`, error); };
        this.#worker.addEventListener("error", onWorkerError);
        this.#worker.addEventListener("messageerror", onWorkerError);
        const onClose = () => {
            this.readyState = this.CLOSED;
            this.dispatchEvent(new MessageEvent("close"));
        };
        const onReconnect = () => {
            this.readyState = this.CLOSED;
            this.dispatchEvent(new MessageEvent("reconnect"));
        };
        this.#worker.addEventListener("message", (event) => {
            switch (event.data.type) {
                case "open": {
                    this.readyState = this.OPEN;
                    this.#connectionClosed = false;
                    this.dispatchEvent(new MessageEvent("open"));
                    break;
                }
                case "close": {
                    this.#worker.terminate();
                    onClose();
                    break;
                }
                case "reconnect": {
                    onReconnect();
                    break;
                }
                case "error": {
                    this.dispatchEvent(new ErrorEvent("error", event.data));
                    break;
                }
                case "disconnect": {
                    this.dispatchEvent(new ErrorEvent("disconnect", event.data));
                    break;
                }
                case "message": {
                    this.dispatchEvent(new MessageEvent("message", event.data));
                    break;
                }
                case "channels": {
                    this.#eventData.channels = event.data.data;
                    this.dispatchEvent(new MessageEvent("channels", event.data));
                    break;
                }
                case "services": {
                    this.#eventData.services = event.data.data;
                    this.dispatchEvent(new MessageEvent("services", event.data));
                    break;
                }
                case "service_response": {
                    // Each service request has its own response listener
                    break;
                }
                default: {
                    console.warn(`Foxglove Ws Adapter: Worker sent unexpected message`, event.data);
                }
            }
        });
    }
    constructor(data, worker) {
        super();
        this.url = 'wsUrl' in data ? data.wsUrl : '';
        this.#worker = worker;
        this.#handleMessageEvents();
        this.#sendToWorker({ type: "open", data });
    }
    on = (name, listener) => {
        this.addEventListener(name, listener);
        if (name in this.#eventData) {
            const data = this.#eventData[name];
            if (data) {
                this.dispatchEvent(new MessageEvent(name, { data }));
            }
        }
    };
    off = (name, listener) => {
        this.removeEventListener(name, listener);
    };
    close() {
        if (!this.#connectionClosed) {
            this.#connectionClosed = true;
            this.#sendToWorker({ type: "close", data: null });
            this.dispatchEvent(new Event("close"));
        }
    }
    subscribe({ topics }) {
        this.#sendToWorker({ type: 'subscribe', data: topics });
    }
    unsubscribe({ topics }) {
        this.#sendToWorker({ type: 'unsubscribe', data: topics });
    }
    async request({ name, timeout = REQUEST_RESPONSE_TIMEOUT, ignoreResponse = false, body = {} }) {
        return await new Promise((resolve) => {
            const serviceCallId = ++this.#serviceCallId;
            const listener = ({ data: message }) => {
                if (message.type !== 'service_response') {
                    return;
                }
                if (message.data.serviceCallId === serviceCallId) {
                    clearTimeout(rejectionTimer);
                    if (!message.data.ok) {
                        console.error(`Service "${name}" response failed`, message.data.error);
                        resolve(null);
                    }
                    else {
                        this.#worker.removeEventListener("message", listener);
                        resolve(message.data.buffer);
                    }
                }
            };
            if (!ignoreResponse) {
                this.#worker.addEventListener("message", listener);
            }
            this.#sendToWorker({
                type: "call_service",
                data: { name, serviceCallId, body }
            });
            const rejectionTimer = setTimeout(() => {
                console.error(`Service "${name}"@${serviceCallId} response timeout (${timeout / 1_000}s)`);
                resolve(null);
            }, timeout);
            if (ignoreResponse) {
                clearTimeout(rejectionTimer);
                resolve(null);
            }
        });
    }
    send(data) {
        console.error('Foxglove Ws Aparter Error: raw send was requested', data);
        // this.#sendToWorker({ type: "send", data: data as any });
    }
}
//# sourceMappingURL=adapter.js.map