/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Tiles
 */
import { BentleyError, BentleyStatus, Logger } from "@itwin/core-bentley";
import { RealityDataFormat, RealityDataProvider } from "@itwin/core-common";
import { FrontendLoggerCategory } from "./common/FrontendLoggerCategory";
import { CesiumIonAssetProvider, ContextShareProvider, getCesiumAssetUrl } from "./tile/internal";
import { RealityDataSourceTilesetUrlImpl } from "./RealityDataSourceTilesetUrlImpl";
import { RealityDataSourceContextShareImpl } from "./RealityDataSourceContextShareImpl";
import { RealityDataSourceCesiumIonAssetImpl } from "./RealityDataSourceCesiumIonAssetImpl";
import { RealityDataSourceGoogle3dTilesImpl } from "./internal/RealityDataSourceGoogle3dTilesImpl";
import { IModelApp } from "./IModelApp";
import { getCopyrights, GoogleMapsDecorator } from "./internal/GoogleMapsDecorator";
const loggerCategory = FrontendLoggerCategory.RealityData;
/**
 * Reality Data Operation error
 * @alpha
 */
export class RealityDataError extends BentleyError {
    constructor(errorNumber, message, getMetaData) {
        super(errorNumber, message, getMetaData);
    }
}
/** Utility functions for RealityDataSource
 * @beta
 */
export var RealityDataSource;
(function (RealityDataSource) {
    /** Create a RealityDataSourceKey from a tilesetUrl.
     * @param tilesetUrl the reality data attachment url
     * @param inputProvider identify the RealityDataProvider if known, otherwise function will try to extract it from the tilesetUrl
     * @param inputFormat identify the RealityDataFormat if known, otherwise function will try to extract it from the tilesetUrl
     * @returns the RealityDataSourceKey that uniquely identify a reality data for a provider
     */
    function createKeyFromUrl(tilesetUrl, inputProvider, inputFormat) {
        let format = inputFormat ? inputFormat : RealityDataFormat.fromUrl(tilesetUrl);
        if (CesiumIonAssetProvider.isProviderUrl(tilesetUrl)) {
            const provider = RealityDataProvider.CesiumIonAsset;
            let cesiumIonAssetKey = { provider, format, id: CesiumIonAssetProvider.osmBuildingId }; // default OSM building
            // Parse URL to extract possible asset id and key if provided
            const cesiumAsset = CesiumIonAssetProvider.parseCesiumUrl(tilesetUrl);
            if (cesiumAsset) {
                cesiumIonAssetKey = RealityDataSource.createCesiumIonAssetKey(cesiumAsset.id, cesiumAsset.key);
            }
            return cesiumIonAssetKey;
        }
        // Try to extract realityDataId from URL and if not possible, use the url as the key
        if (ContextShareProvider.isProviderUrl(tilesetUrl)) {
            const info = ContextShareProvider.getInfoFromUrl(tilesetUrl);
            const provider = inputProvider ? inputProvider : info.provider;
            format = inputFormat ? inputFormat : info.format;
            const contextShareKey = { provider, format, id: info.id, iTwinId: info.iTwinId };
            return contextShareKey;
        }
        // default to tileSetUrl
        const provider2 = inputProvider ? inputProvider : RealityDataProvider.TilesetUrl;
        const urlKey = { provider: provider2, format, id: tilesetUrl };
        return urlKey;
    }
    RealityDataSource.createKeyFromUrl = createKeyFromUrl;
    /** @alpha - was used for a very specific case of point cloud (opc) attachment that should not be made public */
    function createKeyFromBlobUrl(blobUrl, inputProvider, inputFormat) {
        const info = ContextShareProvider.getInfoFromBlobUrl(blobUrl);
        const format = inputFormat ? inputFormat : info.format;
        const provider = inputProvider ? inputProvider : info.provider;
        const contextShareKey = { provider, format, id: info.id };
        return contextShareKey;
    }
    RealityDataSource.createKeyFromBlobUrl = createKeyFromBlobUrl;
    /** @alpha - OrbitGtBlobProps is alpha */
    function createKeyFromOrbitGtBlobProps(orbitGtBlob, inputProvider, inputFormat) {
        const format = inputFormat ? inputFormat : RealityDataFormat.OPC;
        if (orbitGtBlob.blobFileName && orbitGtBlob.blobFileName.toLowerCase().startsWith("http")) {
            return RealityDataSource.createKeyFromBlobUrl(orbitGtBlob.blobFileName, inputProvider, format);
        }
        else if (orbitGtBlob.rdsUrl) {
            return RealityDataSource.createKeyFromUrl(orbitGtBlob.rdsUrl, inputProvider, format);
        }
        const provider = inputProvider ? inputProvider : RealityDataProvider.OrbitGtBlob;
        const id = `${orbitGtBlob.accountName}:${orbitGtBlob.containerName}:${orbitGtBlob.blobFileName}:?${orbitGtBlob.sasToken}`;
        return { provider, format, id };
    }
    RealityDataSource.createKeyFromOrbitGtBlobProps = createKeyFromOrbitGtBlobProps;
    /** @alpha - OrbitGtBlobProps is alpha */
    function createOrbitGtBlobPropsFromKey(rdSourceKey) {
        if (rdSourceKey.provider !== RealityDataProvider.OrbitGtBlob)
            return undefined;
        const splitIds = rdSourceKey.id.split(":");
        const sasTokenIndex = rdSourceKey.id.indexOf(":?");
        const sasToken = rdSourceKey.id.substring(sasTokenIndex + 2);
        const orbitGtBlob = {
            accountName: splitIds[0],
            containerName: splitIds[1],
            blobFileName: splitIds[2],
            sasToken,
        };
        return orbitGtBlob;
    }
    RealityDataSource.createOrbitGtBlobPropsFromKey = createOrbitGtBlobPropsFromKey;
    /** @internal - Is used by "fdt attach cesium asset" keyin*/
    function createCesiumIonAssetKey(osmAssetId, requestKey) {
        const id = getCesiumAssetUrl(osmAssetId, requestKey);
        return { provider: RealityDataProvider.CesiumIonAsset, format: RealityDataFormat.ThreeDTile, id };
    }
    RealityDataSource.createCesiumIonAssetKey = createCesiumIonAssetKey;
    /** Return an instance of a RealityDataSource from a source key.
     * There will aways be only one reality data RealityDataSource for a corresponding reality data source key.
     * @alpha
     */
    async function fromKey(key, iTwinId) {
        const provider = IModelApp.realityDataSourceProviders.find(key.provider);
        if (!provider) {
            Logger.logWarning(loggerCategory, `RealityDataSourceProvider "${key.provider}" is not registered`);
            return undefined;
        }
        return provider.createRealityDataSource(key, iTwinId);
    }
    RealityDataSource.fromKey = fromKey;
})(RealityDataSource || (RealityDataSource = {}));
/** A registry of [[RealityDataSourceProvider]]s identified by their unique names. The registry can be accessed via [[IModelApp.realityDataSourceProviders]].
 * It includes a handful of built-in providers for sources like Cesium ION, ContextShare, OrbitGT, and arbitrary public-accessible URLs.
 * Any number of additional providers can be registered. They should typically be registered just after [[IModelApp.startup]].
 * @beta
 */
export class RealityDataSourceProviderRegistry {
    _providers = new Map();
    /** @internal */
    constructor() {
        this.register(RealityDataProvider.CesiumIonAsset, {
            createRealityDataSource: async (key, iTwinId) => RealityDataSourceCesiumIonAssetImpl.createFromKey(key, iTwinId),
        });
        this.register(RealityDataProvider.TilesetUrl, {
            createRealityDataSource: async (key, iTwinId) => RealityDataSourceTilesetUrlImpl.createFromKey(key, iTwinId),
        });
        this.register(RealityDataProvider.ContextShare, {
            createRealityDataSource: async (key, iTwinId) => RealityDataSourceContextShareImpl.createFromKey(key, iTwinId),
        });
        this.register(RealityDataProvider.OrbitGtBlob, {
            // ###TODO separate TilesetUrlImpl
            createRealityDataSource: async (key, iTwinId) => RealityDataSourceTilesetUrlImpl.createFromKey(key, iTwinId),
        });
    }
    /** Register `provider` to produce [[RealityDataSource]]s for the specified provider `name`. */
    register(name, provider) {
        this._providers.set(name, provider);
    }
    /** Look up the provider registered by the specified `name`. */
    find(name) {
        return this._providers.get(name);
    }
}
/**
 * Will provide Google Photorealistic 3D Tiles (in 3dTile format).
 * A valid API key or getAuthToken fuction must be supplied when creating this provider.
 * To use this provider, you must register it with [[IModelApp.realityDataSourceProviders]].
 * Example usage:
 * ```ts
 * [[include:GooglePhotorealistic3dTiles_providerApiKey]]
 * ```
 * @see [Google Photorealistic 3D Tiles]($docs/learning/frontend/GooglePhotorealistic3dTiles.md)
 * @beta
 */
export class Google3dTilesProvider {
    /** Google Map Tiles API Key used to access Google 3D Tiles. */
    _apiKey;
    /** Function that returns an OAuth token for authenticating with Google 3D Tiles. This token is expected to not contain the "Bearer" prefix. */
    _getAuthToken;
    /** Decorator for Google Maps logos. */
    _decorator;
    /** Enables cached decorations for this provider. @see [[ViewportDecorator.useCachedDecorations]] */
    useCachedDecorations = true;
    async createRealityDataSource(key, iTwinId) {
        if (!this._apiKey && !this._getAuthToken) {
            Logger.logError(loggerCategory, "Either an API key or getAuthToken function are required to create a Google3dTilesProvider.");
            return undefined;
        }
        return RealityDataSourceGoogle3dTilesImpl.createFromKey(key, iTwinId, this._apiKey, this._getAuthToken);
    }
    constructor(options) {
        this._apiKey = options.apiKey;
        this._getAuthToken = options.getAuthToken;
        this._decorator = new GoogleMapsDecorator(options.showCreditsOnScreen ?? true);
    }
    /**
     * Initialize the Google 3D Tiles reality data source provider by activating its decorator, which consists of loading the correct Google Maps logo.
     * @returns `true` if the decorator was successfully activated, otherwise `false`.
     */
    async initialize() {
        const isActivated = await this._decorator.activate("satellite");
        if (!isActivated) {
            const msg = "Failed to activate decorator";
            Logger.logError(loggerCategory, msg);
            throw new BentleyError(BentleyStatus.ERROR, msg);
        }
        return isActivated;
    }
    decorate(_context) {
        this._decorator.decorate(_context);
    }
    async addAttributions(cards, vp) {
        const copyrightMap = getCopyrights(vp);
        // Only add another logo card if the tiles have copyright
        if (copyrightMap.size > 0) {
            // Order by most occurances to least
            // See https://developers.google.com/maps/documentation/tile/create-renderer#display-attributions
            const sortedCopyrights = [...copyrightMap.entries()].sort((a, b) => b[1] - a[1]);
            let copyrightMsg = "Data provided by:<br><ul>";
            copyrightMsg += sortedCopyrights.map(([key]) => `<li>${key}</li>`).join("");
            copyrightMsg += "</ul>";
            const iconSrc = document.createElement("img");
            iconSrc.src = `${IModelApp.publicPath}images/GoogleMaps_Logo_Gray.svg`;
            iconSrc.style.padding = "10px 10px 5px 10px";
            cards.appendChild(IModelApp.makeLogoCard({
                iconSrc,
                iconWidth: 98,
                heading: "Google Photorealistic 3D Tiles",
                notice: copyrightMsg
            }));
        }
    }
}
/** Returns the URL used for retrieving Google Photorealistic 3D Tiles.
 * @beta
 */
export function getGoogle3dTilesUrl() {
    return "https://tile.googleapis.com/v1/3dtiles/root.json";
}
//# sourceMappingURL=RealityDataSource.js.map