"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
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.IModelTileRpcImpl = void 0;
exports.cancelTileContentRequests = cancelTileContentRequests;
const core_bentley_1 = require("@itwin/core-bentley");
const core_common_1 = require("@itwin/core-common");
const BackendLoggerCategory_1 = require("../BackendLoggerCategory");
const IModelDb_1 = require("../IModelDb");
const IModelHost_1 = require("../IModelHost");
const PromiseMemoizer_1 = require("../PromiseMemoizer");
const tracing_1 = require("../rpc/tracing");
const RpcBriefcaseUtility_1 = require("./RpcBriefcaseUtility");
const NativePlatform_1 = require("../internal/NativePlatform");
const Symbols_1 = require("../internal/Symbols");
function generateTileRequestKey(props) {
    const token = props.tokenProps;
    return `${JSON.stringify({
        key: token.key,
        iTwinId: token.iTwinId,
        iModelId: token.iModelId,
        changesetId: token.changeset?.id,
    })}:${props.treeId}`;
}
class TileRequestMemoizer extends PromiseMemoizer_1.PromiseMemoizer {
    _loggerCategory = BackendLoggerCategory_1.BackendLoggerCategory.IModelTileRequestRpc;
    makeMetadata(props) {
        const meta = { ...props.tokenProps };
        this.addMetadata(meta, props);
        return meta;
    }
    constructor(memoizeFn, generateKeyFn) {
        super(memoizeFn, generateKeyFn);
    }
    memoize(props) {
        return super.memoize(props);
    }
    deleteMemoized(props) {
        super.deleteMemoized(props);
    }
    log(status, props) {
        const descr = `${this._operationName}(${this.stringify(props)})`;
        core_bentley_1.Logger.logTrace(this._loggerCategory, `Backend ${status} ${descr}`, () => this.makeMetadata(props));
    }
    async perform(props) {
        this.log("received", props);
        const tileQP = this.memoize(props);
        await core_bentley_1.BeDuration.race(this._timeoutMilliseconds, tileQP.promise).catch(() => { });
        // Note: Rejections must be caught so that the memoization entry can be deleted
        if (tileQP.isPending) {
            this.log("issuing pending status for", props);
            throw new core_common_1.RpcPendingResponse(); // eslint-disable-line @typescript-eslint/only-throw-error
        }
        this.deleteMemoized(props);
        if (tileQP.isFulfilled) {
            this.log("completed", props);
            (0, core_bentley_1.assert)(undefined !== tileQP.result);
            return tileQP.result;
        }
        (0, core_bentley_1.assert)(tileQP.isRejected);
        this.log("rejected", props);
        throw tileQP.error;
    }
}
async function getTileTreeProps(props) {
    (0, core_bentley_1.assert)(undefined !== props.accessToken);
    const db = await RpcBriefcaseUtility_1.RpcBriefcaseUtility.findOpenIModel(props.accessToken, props.tokenProps);
    return db.tiles.requestTileTreeProps(props.treeId);
}
class RequestTileTreePropsMemoizer extends TileRequestMemoizer {
    get _timeoutMilliseconds() { return IModelHost_1.IModelHost.tileTreeRequestTimeout; }
    get _operationName() { return "requestTileTreeProps"; }
    stringify(props) { return props.treeId; }
    addMetadata(meta, props) {
        meta.treeId = props.treeId;
    }
    static _instance;
    constructor() {
        super(getTileTreeProps, generateTileRequestKey);
        IModelHost_1.IModelHost.onBeforeShutdown.addOnce(() => {
            this[Symbol.dispose]();
            RequestTileTreePropsMemoizer._instance = undefined;
        });
    }
    static async perform(props) {
        if (undefined === this._instance)
            this._instance = new RequestTileTreePropsMemoizer();
        return this._instance.perform(props);
    }
}
async function getTileContent(props) {
    (0, core_bentley_1.assert)(undefined !== props.accessToken);
    const db = await RpcBriefcaseUtility_1.RpcBriefcaseUtility.findOpenIModel(props.accessToken, props.tokenProps);
    const tile = await db.tiles.requestTileContent(props.treeId, props.contentId);
    // ###TODO: Verify the guid supplied by the front-end matches the guid stored in the model?
    if (IModelHost_1.IModelHost.usingExternalTileCache) {
        const tileMetadata = {
            backendName: IModelHost_1.IModelHost.applicationId,
            tileGenerationTime: tile.elapsedSeconds.toString(),
            tileSize: tile.content.byteLength.toString(),
        };
        await IModelHost_1.IModelHost.tileStorage?.uploadTile(props.tokenProps.iModelId ?? db.iModelId, props.tokenProps.changeset?.id ?? db.changeset.id, props.treeId, props.contentId, tile.content, props.guid, tileMetadata);
        const { accessToken: _, ...safeProps } = props;
        core_bentley_1.Logger.logInfo(BackendLoggerCategory_1.BackendLoggerCategory.IModelTileRequestRpc, "Generated and uploaded tile", { tileMetadata, ...safeProps });
        return core_common_1.TileContentSource.ExternalCache;
    }
    return core_common_1.TileContentSource.Backend;
}
function generateTileContentKey(props) {
    return `${generateTileRequestKey(props)}:${props.contentId}`;
}
class RequestTileContentMemoizer extends TileRequestMemoizer {
    get _timeoutMilliseconds() { return IModelHost_1.IModelHost.tileContentRequestTimeout; }
    get _operationName() { return "requestTileContent"; }
    stringify(props) { return `${props.treeId}:${props.contentId}`; }
    addMetadata(meta, props) {
        meta.treeId = props.treeId;
        meta.contentId = props.contentId;
    }
    static _instance;
    constructor() {
        super(getTileContent, generateTileContentKey);
        IModelHost_1.IModelHost.onBeforeShutdown.addOnce(() => {
            this[Symbol.dispose]();
            RequestTileContentMemoizer._instance = undefined;
        });
    }
    static get instance() {
        if (undefined === this._instance)
            this._instance = new RequestTileContentMemoizer();
        return this._instance;
    }
    static async perform(props) {
        return this.instance.perform(props);
    }
}
function currentActivity() {
    return tracing_1.RpcTrace.expectCurrentActivity;
}
/** @internal */
class IModelTileRpcImpl extends core_common_1.RpcInterface {
    static register() { core_common_1.RpcManager.registerImpl(core_common_1.IModelTileRpcInterface, IModelTileRpcImpl); }
    async requestTileTreeProps(tokenProps, treeId) {
        return RequestTileTreePropsMemoizer.perform({ accessToken: currentActivity().accessToken, tokenProps, treeId });
    }
    async purgeTileTrees(tokenProps, modelIds) {
        // `undefined` gets forwarded as `null`...
        if (null === modelIds)
            modelIds = undefined;
        const db = await RpcBriefcaseUtility_1.RpcBriefcaseUtility.findOpenIModel(currentActivity().accessToken, tokenProps);
        if (!db.isOpen) {
            return;
        }
        return db[Symbols_1._nativeDb].purgeTileTrees(modelIds);
    }
    async generateTileContent(tokenProps, treeId, contentId, guid) {
        return RequestTileContentMemoizer.perform({ accessToken: currentActivity().accessToken, tokenProps, treeId, contentId, guid });
    }
    async retrieveTileContent(tokenProps, key) {
        const db = await RpcBriefcaseUtility_1.RpcBriefcaseUtility.findOpenIModel(currentActivity().accessToken, tokenProps);
        return db.tiles.getTileContent(key.treeId, key.contentId);
    }
    async getTileCacheConfig(tokenProps) {
        if (IModelHost_1.IModelHost.tileStorage === undefined)
            return undefined;
        const iModelId = tokenProps.iModelId ?? (await RpcBriefcaseUtility_1.RpcBriefcaseUtility.findOpenIModel(currentActivity().accessToken, tokenProps)).iModelId;
        return IModelHost_1.IModelHost.tileStorage.getDownloadConfig(iModelId);
    }
    async queryVersionInfo() {
        return NativePlatform_1.IModelNative.platform.getTileVersionInfo();
    }
    /** @internal */
    async requestElementGraphics(rpcProps, request) {
        const iModel = await RpcBriefcaseUtility_1.RpcBriefcaseUtility.findOpenIModel(currentActivity().accessToken, rpcProps);
        return iModel.generateElementGraphics(request);
    }
}
exports.IModelTileRpcImpl = IModelTileRpcImpl;
/** @internal */
async function cancelTileContentRequests(tokenProps, contentIds) {
    const iModel = IModelDb_1.IModelDb.findByKey(tokenProps.key);
    const props = { tokenProps, treeId: "", contentId: "" };
    for (const entry of contentIds) {
        props.treeId = entry.treeId;
        for (const contentId of entry.contentIds) {
            props.contentId = contentId;
            RequestTileContentMemoizer.instance.deleteMemoized(props);
        }
        iModel[Symbols_1._nativeDb].cancelTileContentRequests(entry.treeId, entry.contentIds);
    }
}
//# sourceMappingURL=IModelTileRpcImpl.js.map