"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module IpcSocket
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.IpcWebSocketBackend = exports.IpcWebSocketFrontend = exports.IpcWebSocket = exports.IpcWebSocketMessage = exports.IpcWebSocketMessageType = void 0;
/** @internal */
var IpcWebSocketMessageType;
(function (IpcWebSocketMessageType) {
    IpcWebSocketMessageType[IpcWebSocketMessageType["Send"] = 0] = "Send";
    IpcWebSocketMessageType[IpcWebSocketMessageType["Push"] = 1] = "Push";
    IpcWebSocketMessageType[IpcWebSocketMessageType["Invoke"] = 2] = "Invoke";
    IpcWebSocketMessageType[IpcWebSocketMessageType["Response"] = 3] = "Response";
    IpcWebSocketMessageType[IpcWebSocketMessageType["Internal"] = 4] = "Internal";
    IpcWebSocketMessageType[IpcWebSocketMessageType["Duplicate"] = 5] = "Duplicate";
})(IpcWebSocketMessageType || (exports.IpcWebSocketMessageType = IpcWebSocketMessageType = {}));
/** @internal */
var IpcWebSocketMessage;
(function (IpcWebSocketMessage) {
    function internal() {
        return { type: IpcWebSocketMessageType.Internal, channel: "", sequence: Number.MIN_SAFE_INTEGER };
    }
    IpcWebSocketMessage.internal = internal;
    function duplicate() {
        return { type: IpcWebSocketMessageType.Duplicate, channel: "", sequence: Number.MIN_SAFE_INTEGER };
    }
    IpcWebSocketMessage.duplicate = duplicate;
    function skip(message) {
        return message.type === IpcWebSocketMessageType.Internal || message.type === IpcWebSocketMessageType.Duplicate;
    }
    IpcWebSocketMessage.skip = skip;
})(IpcWebSocketMessage || (exports.IpcWebSocketMessage = IpcWebSocketMessage = {}));
/** @internal */
class IpcWebSocket {
    constructor() {
        this._channels = new Map();
        IpcWebSocket.receivers.add(async (e, m) => this.broadcast(e, m));
    }
    addListener(channel, listener) {
        let listeners = this._channels.get(channel);
        if (!listeners) {
            listeners = new Set();
            this._channels.set(channel, listeners);
        }
        if (!listeners.has(listener))
            listeners.add(listener);
        return () => listeners.delete(listener);
    }
    removeListener(channel, listener) {
        this._channels.get(channel)?.delete(listener);
    }
    async broadcast(evt, message) {
        if (message.type !== IpcWebSocketMessageType.Send && message.type !== IpcWebSocketMessageType.Push)
            return;
        const handlers = this._channels.get(message.channel);
        if (!handlers)
            return;
        let arg = message.data;
        if (typeof (arg) === "undefined")
            arg = [];
        for (const handler of handlers)
            handler(evt, ...arg);
    }
}
exports.IpcWebSocket = IpcWebSocket;
IpcWebSocket.receivers = new Set();
/** @internal */
class IpcWebSocketFrontend extends IpcWebSocket {
    constructor() {
        super();
        this._nextRequest = 0;
        this._pendingRequests = new Map();
        IpcWebSocket.receivers.add(async (e, m) => this.dispatch(e, m));
    }
    send(channel, ...data) {
        IpcWebSocket.transport.send({ type: IpcWebSocketMessageType.Send, channel, data, sequence: -1 });
    }
    async invoke(channel, methodName, ...args) {
        const requestId = ++this._nextRequest;
        IpcWebSocket.transport.send({ type: IpcWebSocketMessageType.Invoke, channel, method: methodName, data: args, request: requestId, sequence: -1 });
        return new Promise((resolve) => {
            this._pendingRequests.set(requestId, resolve);
        });
    }
    async dispatch(_evt, message) {
        if (message.type !== IpcWebSocketMessageType.Response || !message.response)
            return;
        const pendingHandler = this._pendingRequests.get(message.response);
        if (!pendingHandler)
            return;
        this._pendingRequests.delete(message.response);
        pendingHandler(message.data);
    }
}
exports.IpcWebSocketFrontend = IpcWebSocketFrontend;
/** @internal */
class IpcWebSocketBackend extends IpcWebSocket {
    constructor() {
        super();
        this._handlers = new Map();
        this._processingQueue = [];
        IpcWebSocket.receivers.add(async (e, m) => this.dispatch(e, m));
    }
    send(channel, ...data) {
        IpcWebSocket.transport.send({ type: IpcWebSocketMessageType.Push, channel, data, sequence: -1 });
    }
    handle(channel, handler) {
        this._handlers.set(channel, handler);
        return () => {
            if (this._handlers.get(channel) === handler)
                this._handlers.delete(channel);
        };
    }
    async dispatch(_evt, message) {
        if (message.type !== IpcWebSocketMessageType.Invoke)
            return;
        this._processingQueue.push(message);
        await this.processMessages();
    }
    async processMessages() {
        if (!this._processingQueue.length) {
            return;
        }
        const message = this._processingQueue.shift();
        if (message && message.method) {
            const handler = this._handlers.get(message.channel);
            if (handler) {
                let args = message.data;
                if (typeof (args) === "undefined")
                    args = [];
                const response = await handler({}, message.method, ...args);
                IpcWebSocket.transport.send({
                    type: IpcWebSocketMessageType.Response,
                    channel: message.channel,
                    response: message.request,
                    data: response,
                    sequence: -1,
                });
            }
        }
        await this.processMessages();
    }
}
exports.IpcWebSocketBackend = IpcWebSocketBackend;
//# sourceMappingURL=IpcWebSocket.js.map