/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { Cartographic, EcefLocation } from "@itwin/core-common";
import { Range3d } from "@itwin/core-geometry";
import { CRSManager, Downloader, DownloaderXhr, OnlineEngine, OPCReader, OrbitGtBounds, PageCachedFile, UrlFS } from "@itwin/core-orbitgt";
import { FrontendLoggerCategory } from "../../common/FrontendLoggerCategory";
import { BentleyError, expectDefined, Logger, RealityDataStatus } from "@itwin/core-bentley";
import { RealityDataError } from "../../RealityDataSource";
const loggerCategory = FrontendLoggerCategory.RealityData;
/**
 * This class provide methods used to interpret Orbit Point Cloud (OPC) format
 */
export class OPCFormatInterpreter {
    /** Gets an OPC file reader from a blobFileUrl
     * @param blobFileURL the name of the file.
     * @returns return a file reader open to read provided blob file
     */
    static async getFileReaderFromBlobFileURL(blobFileURL) {
        if (Downloader.INSTANCE == null)
            Downloader.INSTANCE = new DownloaderXhr();
        if (CRSManager.ENGINE == null)
            CRSManager.ENGINE = await OnlineEngine.create();
        // let blobFileURL: string = rdUrl;
        // if (accountName.length > 0) blobFileURL = UrlFS.getAzureBlobSasUrl(opcConfig.accountName, opcConfig.containerName, opcConfig.blobFileName, opcConfig.sasToken);
        const urlFS = new UrlFS();
        // wrap a caching layer (16 MB) around the blob file
        const blobFileSize = await urlFS.getFileLength(blobFileURL);
        Logger.logTrace(loggerCategory, `OPC File Size is ${blobFileSize.toString()}`);
        const blobFile = new PageCachedFile(urlFS, blobFileURL, blobFileSize, 128 * 1024 /* pageSize */, 128 /* maxPageCount */);
        const fileReader = await OPCReader.openFile(blobFile, blobFileURL, true /* lazyLoading */);
        return fileReader;
    }
    /** Gets reality data spatial location and extents
     * @param fileReader a file reader instance obtains from call to getFileReaderFromBlobFileURL
     * @returns spatial location and volume of interest, in meters, centered around `spatial location`
     * @throws [[RealityDataError]] if source is invalid or cannot be read
     */
    static async getSpatialLocationAndExtents(fileReader) {
        let worldRange = new Range3d();
        let location;
        let isGeolocated = true;
        const bounds = fileReader.getFileBounds();
        worldRange = Range3d.createXYZXYZ(bounds.getMinX(), bounds.getMinY(), bounds.getMinZ(), bounds.getMaxX(), bounds.getMaxY(), bounds.getMaxZ());
        isGeolocated = false;
        const fileCrs = fileReader.getFileCRS();
        // the CRS 9300 is not defined in the CRS registry database, so we cannot use CRSManager
        // Check to isGeographicCRS and isProjectedCRS are both going to return false in that case and we will fallback to
        // use un-georeferenced code path.
        await CRSManager.ENGINE.prepareForArea(fileCrs, bounds);
        const isGeographicCRS = CRSManager.ENGINE.isGeographicCRS(fileCrs);
        const isProjectedCRS = CRSManager.ENGINE.isProjectedCRS(fileCrs);
        if (fileCrs && (isProjectedCRS || isGeographicCRS)) {
            try {
                const wgs84ECEFCrs = "4978";
                await CRSManager.ENGINE.prepareForArea(wgs84ECEFCrs, new OrbitGtBounds());
                const ecefBounds = CRSManager.transformBounds(bounds, fileCrs, wgs84ECEFCrs);
                const ecefRange = Range3d.createXYZXYZ(ecefBounds.getMinX(), ecefBounds.getMinY(), ecefBounds.getMinZ(), ecefBounds.getMaxX(), ecefBounds.getMaxY(), ecefBounds.getMaxZ());
                const ecefCenter = expectDefined(ecefRange.localXYZToWorld(.5, .5, .5));
                const cartoCenter = expectDefined(Cartographic.fromEcef(ecefCenter));
                cartoCenter.height = 0;
                const ecefLocation = EcefLocation.createFromCartographicOrigin(cartoCenter);
                location = ecefLocation;
                // this.iModelDb.setEcefLocation(ecefLocation);
                const ecefToWorld = expectDefined(ecefLocation.getTransform().inverse());
                worldRange = ecefToWorld.multiplyRange(ecefRange);
                isGeolocated = true;
            }
            catch (e) {
                Logger.logWarning(loggerCategory, `Error getSpatialLocationAndExtents - cannot interpret point cloud`);
                const errorProps = BentleyError.getErrorProps(e);
                const getMetaData = () => {
                    return { errorProps };
                };
                const error = new RealityDataError(RealityDataStatus.InvalidData, "Invalid or unknown data", getMetaData);
                throw error;
            }
        }
        else {
            // NoGCS case
            isGeolocated = false;
            const centerOfEarth = new EcefLocation({ origin: { x: 0.0, y: 0.0, z: 0.0 }, orientation: { yaw: 0.0, pitch: 0.0, roll: 0.0 } });
            location = centerOfEarth;
            Logger.logTrace(loggerCategory, "OPC RealityData NOT Geolocated", () => ({ ...location }));
        }
        const spatialLocation = { location, worldRange, isGeolocated };
        return spatialLocation;
    }
}
//# sourceMappingURL=OPCFormatInterpreter.js.map