/*---------------------------------------------------------------------------------------------
* 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 { request } from "./request/Request";
import { assert, Logger } from "@itwin/core-bentley";
import { RealityDataFormat, RealityDataProvider } from "@itwin/core-common";
import { FrontendLoggerCategory } from "./common/FrontendLoggerCategory";
import { IModelApp } from "./IModelApp";
import { OPCFormatInterpreter, ThreeDTileFormatInterpreter } from "./tile/internal";
/** This class provides access to the reality data provider services.
 * It encapsulates access to a reality data weiter it be from local access, http or ProjectWise Context Share.
 * The key provided at the creation determines if this is ProjectWise Context Share reference.
 * If not then it is considered local (ex: C:\temp\TileRoot.json) or plain http access (http://someserver.com/data/TileRoot.json)
 * There is a one to one relationship between a reality data and the instances of present class.
* @internal
*/
export class RealityDataSourceContextShareImpl {
    /** Construct a new reality data source.
     * @param props JSON representation of the reality data source
     */
    constructor(props) {
        this._isUrlResolved = false;
        /** For use by all Reality Data. For RD stored on PW Context Share, represents the portion from the root of the Azure Blob Container*/
        this._baseUrl = "";
        // this implementaiton is specific to ContextShare provider
        assert(props.sourceKey.provider === RealityDataProvider.ContextShare);
        this.key = props.sourceKey;
        this._isUrlResolved = false;
    }
    /**
     * Create an instance of this class from a source key and iTwin context/
     */
    static async createFromKey(sourceKey, iTwinId) {
        if (sourceKey.provider !== RealityDataProvider.ContextShare)
            return undefined;
        const rdSource = new RealityDataSourceContextShareImpl({ sourceKey });
        let tilesetUrl;
        try {
            await rdSource.queryRealityData(iTwinId);
            tilesetUrl = await rdSource.getServiceUrl(iTwinId);
        }
        catch { }
        return (tilesetUrl !== undefined) ? rdSource : undefined;
    }
    get isContextShare() {
        return (this.key.provider === RealityDataProvider.ContextShare);
    }
    /**
     * Returns Reality Data if available
    */
    get realityData() {
        return this._rd;
    }
    get realityDataId() {
        const realityDataId = this.key.id;
        return realityDataId;
    }
    /**
     * Returns Reality Data type if available
     */
    get realityDataType() {
        return this._rd?.type;
    }
    /**
     * Query Reality Data from provider
     */
    async queryRealityData(iTwinId) {
        if (!this._rd) {
            const token = await IModelApp.getAccessToken();
            if (token && this.realityDataId) {
                if (undefined === IModelApp.realityDataAccess)
                    throw new Error("Missing an implementation of RealityDataAccess on IModelApp, it is required to access reality data. Please provide an implementation to the IModelApp.startup using IModelAppOptions.realityDataAccess.");
                this._rd = await IModelApp.realityDataAccess.getRealityData(token, iTwinId, this.realityDataId);
                // A reality data that has not root document set should not be considered.
                const rootDocument = this._rd.rootDocument ?? "";
                this.setBaseUrl(rootDocument);
            }
        }
    }
    // This is to set the root url from the provided root document path.
    // If the root document is stored on PW Context Share then the root document property of the Reality Data is provided,
    // otherwise the full path to root document is given.
    // The base URL contains the base URL from which tile relative path are constructed.
    // The tile's path root will need to be reinserted for child tiles to return a 200
    setBaseUrl(url) {
        const urlParts = url.split("/");
        urlParts.pop();
        if (urlParts.length === 0)
            this._baseUrl = "";
        else
            this._baseUrl = `${urlParts.join("/")}/`;
    }
    /**
     * Gets a tileset's app data json
     * @param name name or path of tile
     * @returns app data json object
     * @internal
     */
    async getRealityDataTileJson(accessToken, name, realityData) {
        const url = await realityData.getBlobUrl(accessToken, name);
        return request(url.toString(), "json");
    }
    /**
     * This method returns the URL to access the actual 3d tiles from the service provider.
     * @returns string containing the URL to reality data.
     */
    async getServiceUrl(iTwinId) {
        // If url was not resolved - resolve it
        if (!this._isUrlResolved) {
            const rdSourceKey = this.key;
            // we need to resolve tilesetURl from realityDataId and iTwinId
            if (undefined === IModelApp.realityDataAccess)
                throw new Error("Missing an implementation of RealityDataAccess on IModelApp, it is required to access reality data. Please provide an implementation to the IModelApp.startup using IModelAppOptions.realityDataAccess.");
            try {
                const resolvedITwinId = iTwinId ? iTwinId : rdSourceKey.iTwinId;
                this._tilesetUrl = await IModelApp.realityDataAccess.getRealityDataUrl(resolvedITwinId, rdSourceKey.id);
                this._isUrlResolved = true;
            }
            catch {
                const errMsg = `Error getting URL from ContextShare using realityDataId=${rdSourceKey.id} and iTwinId=${iTwinId}`;
                Logger.logError(FrontendLoggerCategory.RealityData, errMsg);
            }
        }
        return this._tilesetUrl;
    }
    async getRootDocument(_iTwinId) {
        const token = await IModelApp.getAccessToken();
        if (token) {
            const realityData = this.realityData;
            if (!realityData)
                throw new Error(`Reality Data not defined`);
            if (!realityData.rootDocument)
                throw new Error(`Root document not defined for reality data: ${realityData.id}`);
            return this.getRealityDataTileJson(token, realityData.rootDocument, realityData);
        }
    }
    /**
     * Gets tile content
     * @param name name or path of tile
     * @returns array buffer of tile content
     */
    async getRealityDataTileContent(accessToken, name, realityData) {
        const url = await realityData.getBlobUrl(accessToken, name);
        return request(url.toString(), "arraybuffer");
    }
    /**
     * Returns the tile content. The path to the tile is relative to the base url of present reality data whatever the type.
     */
    async getTileContent(name) {
        const token = await IModelApp.getAccessToken();
        const tileUrl = this._baseUrl + name;
        if (this.realityData) {
            return this.getRealityDataTileContent(token, tileUrl, this.realityData);
        }
        return undefined;
    }
    /**
     * Returns the tile content in json format. The path to the tile is relative to the base url of present reality data whatever the type.
     */
    async getTileJson(name) {
        const token = await IModelApp.getAccessToken();
        const tileUrl = this._baseUrl + name;
        if (this.realityData) {
            return this.getRealityDataTileJson(token, tileUrl, this.realityData);
        }
        return undefined;
    }
    getTileContentType(url) {
        return url.endsWith("json") ? "tileset" : "tile";
    }
    /**
     * Gets spatial location and extents of this reality data source
     * @returns spatial location and extents
     * @internal
     */
    async getSpatialLocationAndExtents() {
        let spatialLocation;
        const fileType = this.realityDataType;
        // Mapping Resource are not currenlty supported
        if (fileType === "OMR")
            return undefined;
        if (this.key.format === RealityDataFormat.ThreeDTile) {
            const rootDocument = await this.getRootDocument(undefined);
            spatialLocation = ThreeDTileFormatInterpreter.getSpatialLocationAndExtents(rootDocument);
        }
        else if (this.key.format === RealityDataFormat.OPC) {
            if (this.realityData === undefined)
                return undefined;
            const token = await IModelApp.getAccessToken();
            const docRootName = this.realityData.rootDocument;
            if (!docRootName)
                return undefined;
            const blobUrl = await this.realityData.getBlobUrl(token, docRootName);
            if (!blobUrl)
                return undefined;
            const blobStringUrl = blobUrl.toString();
            const filereader = await OPCFormatInterpreter.getFileReaderFromBlobFileURL(blobStringUrl);
            spatialLocation = await OPCFormatInterpreter.getSpatialLocationAndExtents(filereader);
        }
        return spatialLocation;
    }
    /**
     * Gets information to identify the product and engine that create this reality data
     * Will return undefined if cannot be resolved
     * @returns information to identify the product and engine that create this reality data
     * @alpha
     */
    async getPublisherProductInfo() {
        let publisherInfo;
        if (this.key.format === RealityDataFormat.ThreeDTile) {
            const rootDocument = await this.getRootDocument(undefined);
            publisherInfo = ThreeDTileFormatInterpreter.getPublisherProductInfo(rootDocument);
        }
        return publisherInfo;
    }
}
//# sourceMappingURL=RealityDataSourceContextShareImpl.js.map