/*---------------------------------------------------------------------------------------------
* 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 });
};
import { Base64 } from "js-base64";
import { RpcInterface } from "../../RpcInterface";
import { RpcManager } from "../../RpcManager";
import { RpcConfiguration } from "./RpcConfiguration";
import { RpcOperation } from "./RpcOperation";
import { RpcRegistry } from "./RpcRegistry";
/** An RPC operation control response.
 * @public
 */
export class RpcControlResponse {
    constructor() {
        this.message = "RpcControlResponse";
    }
}
/** A pending RPC operation response.
 * @public
 */
export class RpcPendingResponse extends RpcControlResponse {
    /** Constructs a pending response. */
    constructor(message = "") {
        super();
        this.message = message;
    }
}
/** A RPC operation response.
 * @public
 */
export class RpcNotFoundResponse extends RpcControlResponse {
    constructor() {
        super(...arguments);
        this.message = "Not found";
    }
}
/** Manages requests and responses for an RPC configuration.
 * @internal
 */
export 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 {
                async describeEndpoints() { return this.forward(arguments); }
            },
            __setFunctionName(_a, "_channelInterface"),
            _a.interfaceVersion = "CONTROL",
            _a.interfaceName = "",
            _a);
        this._channelImpl = class extends RpcInterface {
            async describeEndpoints() {
                const endpoints = [];
                this.configuration.interfaces().forEach((definition) => {
                    if (!RpcRegistry.instance.isRpcInterfaceInitialized(definition))
                        return;
                    const description = { interfaceName: definition.interfaceName, interfaceVersion: definition.interfaceVersion, operationNames: [], compatible: true };
                    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 Base64.encode(id);
    }
    activateClient() {
        if (this._clientActive)
            return;
        this.initialize();
        const token = { key: "none", iTwinId: "none", iModelId: "none", changeset: { id: "none" } };
        RpcOperation.forEach(this._channelInterface, (operation) => operation.policy.token = (_request) => RpcOperation.fallbackToken ?? token);
        const client = 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.assign(this._channelInterface, () => this._configuration.constructor);
        RpcManager.registerImpl(this._channelInterface, this._channelImpl);
        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;
    }
}
/** @internal */
RpcControlChannel.channels = [];
RpcControlChannel._obtainLock = 0;
//# sourceMappingURL=RpcControl.js.map