/** @packageDocumentation
 * @module IModelConnection
 */
import { BeEvent, Dictionary, SortedArray } from "@itwin/core-bentley";
import { WritableXYAndZ, XYAndZ, XYZProps } from "@itwin/core-geometry";
import { GeoCoordinatesRequestProps, GeoCoordinatesResponseProps, GeographicCRSProps, IModelCoordinatesRequestProps, IModelCoordinatesResponseProps, PointWithStatus } from "@itwin/core-common";
import { IModelConnection } from "./IModelConnection";
/** Options used to create a [[CoordinateConverter]].
 * @internal exported strictly for tests.
 */
export interface CoordinateConverterOptions {
    isIModelClosed: () => boolean;
    /** Asynchronously convert each point. The resultant array should have the same number and order of points as the input. */
    requestPoints: (points: XYAndZ[]) => Promise<PointWithStatus[]>;
    /** Maximum number of points to include in each request. Default: 300. */
    maxPointsPerRequest?: number;
}
type CoordinateConverterState = "idle" | "scheduled" | "in-flight";
/** Performs conversion of coordinates from one coordinate system to another.
 * A [[GeoConverter]] has a pair of these for converting between iModel coordinates and geographic coordinates.
 * Uses a cache to avoid repeatedly requesting the same points, and a batching strategy to avoid making frequent small requests.
 * The cache stores every point that was ever converted by [[convert]]. It is currently permitted to grow to unbounded size.
 * The batching works as follows:
 *  When a conversion is requested via [[convert]], if all the requested points are in the cache, they are returned immediately.
 *  Otherwise, any points not in the cache and not in the current in-flight request (if any) are placed onto the queue of pending requests.
 *  A pending request is scheduled if one hasn't already been scheduled, via requestAnimationFrame.
 *  In the animation frame callback, the pending requests are split into batches of no more than options.maxPointsPerRequest and dispatched to the backend.
 *  Once the response is received, the results are loaded into and returned from the cache.
 *  If more calls to convert occurred while the request was in flight, another request is dispatched.
 * @internal exported strictly for tests.
 */
export declare class CoordinateConverter {
    protected readonly _cache: Dictionary<XYAndZ, PointWithStatus>;
    protected _state: CoordinateConverterState;
    protected _pending: SortedArray<XYAndZ>;
    protected _inflight: SortedArray<XYAndZ>;
    protected _onCompleted: BeEvent<() => void>;
    protected readonly _scratchXYZ: {
        x: number;
        y: number;
        z: number;
    };
    protected readonly _maxPointsPerRequest: number;
    protected readonly _isIModelClosed: () => boolean;
    protected readonly _requestPoints: (points: XYAndZ[]) => Promise<PointWithStatus[]>;
    protected _redispatchOnCompletion: boolean;
    get isIdle(): boolean;
    protected toXYAndZ(input: XYZProps, output: WritableXYAndZ): XYAndZ;
    constructor(opts: CoordinateConverterOptions);
    protected dispatch(): Promise<void>;
    protected enqueue(points: XYZProps[]): number;
    protected getFromCache(inputs: XYZProps[]): PointWithStatus[];
    protected scheduleDispatch(): Promise<void>;
    convert(inputs: XYZProps[]): Promise<{
        points: PointWithStatus[];
        fromCache: number;
    }>;
    findCached(inputs: XYZProps[]): CachedIModelCoordinatesResponseProps;
}
/** Response to a request to obtain imodel coordinates from cache.
 * @internal
 */
export interface CachedIModelCoordinatesResponseProps {
    /** An array of the same length as the input array, with undefined entries at indices corresponding to points not found in cache. */
    result: Array<PointWithStatus | undefined>;
    /** An array of points in the input array which were not found in the cache, or undefined if all points were found in the cache. */
    missing?: XYZProps[];
}
/** Options used to create a [[GeoConverter]].
 * @internal exported strictly for tests.
 */
export interface GeoConverterOptions {
    readonly datum: string;
    isIModelClosed: () => boolean;
    toIModelCoords: (request: IModelCoordinatesRequestProps) => Promise<PointWithStatus[]>;
    fromIModelCoords: (request: GeoCoordinatesRequestProps) => Promise<PointWithStatus[]>;
}
/** An object capable of communicating with the backend to convert between coordinates in a geographic coordinate system and coordinates in an [[IModelConnection]]'s own coordinate system.
 * @see [[GeoServices.getConverter]] to obtain a converter.
 * @see [GeographicCRS]($common) for more information about geographic coordinate reference systems.
 * @public
 */
export declare class GeoConverter {
    private readonly _geoToIModel;
    private readonly _iModelToGeo;
    /** Used for removing this converter from GeoServices' cache after all requests are completed.
     * @internal
     */
    readonly onAllRequestsCompleted: BeEvent<() => void>;
    /** @internal */
    constructor(opts: GeoConverterOptions);
    /** Convert the specified geographic coordinates into iModel coordinates. */
    convertToIModelCoords(geoPoints: XYZProps[]): Promise<PointWithStatus[]>;
    /** Convert the specified iModel coordinates into geographic coordinates. */
    convertFromIModelCoords(iModelCoords: XYZProps[]): Promise<PointWithStatus[]>;
    /** @internal */
    getIModelCoordinatesFromGeoCoordinates(geoPoints: XYZProps[]): Promise<IModelCoordinatesResponseProps>;
    /** @internal */
    getGeoCoordinatesFromIModelCoordinates(iModelPoints: XYZProps[]): Promise<GeoCoordinatesResponseProps>;
    private checkCompletion;
    /** @internal */
    getCachedIModelCoordinatesFromGeoCoordinates(geoPoints: XYZProps[]): CachedIModelCoordinatesResponseProps;
}
/** @internal */
export type GeoServicesOptions = Omit<GeoConverterOptions, "datum">;
/** The Geographic Services available for an [[IModelConnection]].
 * @see [[IModelConnection.geoServices]] to obtain the GeoServices for a specific iModel.
 * @public
 */
export declare class GeoServices {
    private readonly _options;
    /** Each GeoConverter has its own independent request queue and cache of previously-converted points.
     * Some callers like RealityTileTree obtain a single GeoConverter and reuse it throughout their own lifetime. Therefore they benefit from both batching and caching, and
     * the cache gets deleted once the RealityTileTree becomes disused.
     *
     * Other callers like IModelConnection.spatialToCartographic obtain a new GeoConverter every time they need one, use it to convert a single point(!), and then discard the converter.
     * This entirely prevents batching - e.g., calling spatialToCartographic 20 times in one frame results in 20 http requests.
     * To address that, we cache each GeoConverter returned by getConverter until it has converted at least one point and has no further outstanding conversion requests.
     * In this way, the converter lives for as long as (and no longer than) any caller is awaiting conversion to/from its datum - it and its cache are deleted once it becomes disused.
     * This makes the coordinate caching generally less useful, but at least bounded - and maximizes batching of requests.
     */
    private readonly _cache;
    /** @internal */
    constructor(options: GeoServicesOptions);
    /** @internal */
    static createForIModel(iModel: IModelConnection): GeoServices;
    /** Obtain a converter that can convert between a geographic coordinate system and the iModel's own coordinate system.
     * @param datumOrGCRS The name or JSON representation of the geographic coordinate system datum - for example, "WGS84".
     * @returns a converter, or `undefined` if the iModel is not open.
     * @note A [[BlankConnection]] has no connection to a backend, so it is never "open"; therefore it always returns `undefined`.
     */
    getConverter(datumOrGCRS?: string | GeographicCRSProps): GeoConverter | undefined;
}
export {};
//# sourceMappingURL=GeoServices.d.ts.map