"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 RpcInterface
 */
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
    if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
    return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RpcControlChannel = exports.RpcNotFoundResponse = exports.RpcPendingResponse = exports.RpcControlResponse = void 0;
const js_base64_1 = require("js-base64");
const RpcInterface_1 = require("../../RpcInterface");
const RpcManager_1 = require("../../RpcManager");
const RpcConfiguration_1 = require("./RpcConfiguration");
const RpcOperation_1 = require("./RpcOperation");
const RpcRegistry_1 = require("./RpcRegistry");
/** An RPC operation control response.
 * @public
 */
class RpcControlResponse {
    constructor() {
        this.message = "RpcControlResponse";
    }
}
exports.RpcControlResponse = RpcControlResponse;
/** A pending RPC operation response.
 * @public
 */
class RpcPendingResponse extends RpcControlResponse {
    /** Constructs a pending response. */
    constructor(message = "") {
        super();
        this.message = message;
    }
}
exports.RpcPendingResponse = RpcPendingResponse;
/** A RPC operation response.
 * @public
 */
class RpcNotFoundResponse extends RpcControlResponse {
    constructor() {
        super(...arguments);
        this.message = "Not found";
    }
}
exports.RpcNotFoundResponse = RpcNotFoundResponse;
/** Manages requests and responses for an RPC configuration.
 * @internal
 */
class RpcControlChannel {
    /** @internal */
    static ensureInitialized() {
        this.channels.forEach((channel) => channel.initialize());
    }
    constructor(configuration) {
        var _a;
        this._initialized = false;
        this._clientActive = false;
        this._describeEndpoints = undefined;
        this._channelInterface = (_a = class extends RpcInterface_1.RpcInterface {
                async describeEndpoints() { return this.forward(arguments); }
            },
            __setFunctionName(_a, "_channelInterface"),
            _a.interfaceVersion = "CONTROL",
            _a.interfaceName = "",
            _a);
        this._channelImpl = class extends RpcInterface_1.RpcInterface {
            async describeEndpoints() {
                const endpoints = [];
                this.configuration.interfaces().forEach((definition) => {
                    if (!RpcRegistry_1.RpcRegistry.instance.isRpcInterfaceInitialized(definition))
                        return;
                    const description = { interfaceName: definition.interfaceName, interfaceVersion: definition.interfaceVersion, operationNames: [], compatible: true };
                    RpcOperation_1.RpcOperation.forEach(definition, (operation) => description.operationNames.push(operation.operationName));
                    endpoints.push(description);
                });
                return endpoints;
            }
        };
        this._configuration = configuration;
        RpcControlChannel.channels.push(this);
    }
    /** @internal */
    async describeEndpoints() {
        this.activateClient();
        if (!this._channelInterface.interfaceName) {
            return [];
        }
        return this._describeEndpoints();
    }
    /** @internal */
    static obtain(configuration) {
        if (RpcControlChannel._obtainLock)
            return undefined;
        ++RpcControlChannel._obtainLock;
        const channel = new RpcControlChannel(configuration);
        --RpcControlChannel._obtainLock;
        return channel;
    }
    computeId() {
        const interfaces = [];
        this._configuration.interfaces().forEach((definition) => interfaces.push(`${definition.interfaceName}@${definition.interfaceVersion}`));
        const id = interfaces.sort().join(",");
        return js_base64_1.Base64.encode(id);
    }
    activateClient() {
        if (this._clientActive)
            return;
        this.initialize();
        const token = { key: "none", iTwinId: "none", iModelId: "none", changeset: { id: "none" } };
        RpcOperation_1.RpcOperation.forEach(this._channelInterface, (operation) => operation.policy.token = (_request) => RpcOperation_1.RpcOperation.fallbackToken ?? token);
        const client = RpcManager_1.RpcManager.getClientForInterface(this._channelInterface);
        this._describeEndpoints = async () => client.describeEndpoints();
        this._clientActive = true;
    }
    /** @internal */
    initialize() {
        if (this._initialized) {
            return;
        }
        const id = this.computeId();
        Object.defineProperty(this._channelInterface, "interfaceName", { value: id });
        Object.defineProperty(this._channelImpl, "interfaceName", { value: id });
        RpcConfiguration_1.RpcConfiguration.assign(this._channelInterface, () => this._configuration.constructor);
        RpcManager_1.RpcManager.registerImpl(this._channelInterface, this._channelImpl);
        RpcManager_1.RpcManager.initializeInterface(this._channelInterface);
        this._initialized = true;
    }
    /** @internal */
    handleUnknownOperation(invocation, _error) {
        this.initialize();
        const op = invocation.request.operation;
        if (op.interfaceVersion === "CONTROL" && op.operationName === "describeEndpoints") {
            if (this._channelInterface.interfaceName) {
                op.interfaceDefinition = this._channelInterface.interfaceName;
            }
            return true;
        }
        return false;
    }
}
exports.RpcControlChannel = RpcControlChannel;
/** @internal */
RpcControlChannel.channels = [];
RpcControlChannel._obtainLock = 0;
//# sourceMappingURL=RpcControl.js.map