/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Rendering
 */
import { assert } from "@itwin/core-bentley";
import { Pixel } from "./Pixel";
/** Ensures that the iterable supplied to QueryVisibleFeaturesCallback becomes invalidated once the callback exits.
 * The iterable relies on RenderTarget state that changes from one frame to another.
 */
class ExpiringIterable {
    constructor(features) {
        this._disposed = false;
        this._features = features;
    }
    dispose() {
        this._disposed = true;
        this._features = [];
    }
    [Symbol.iterator]() {
        assert(!this._disposed, "The iterable supplied to QueryVisibleFeaturesCallback is valid only for the duration of the callback.");
        return this._features[Symbol.iterator]();
    }
}
function invokeCallback(features, callback) {
    const iterable = new ExpiringIterable(features);
    try {
        callback(iterable);
    }
    finally {
        iterable.dispose();
    }
}
/** Features read from pixels rendered by a viewport. */
class ScreenFeatures {
    constructor(pixels, rect, viewport) {
        this._pixels = pixels;
        this._rect = rect.clone();
        this._rect.right = viewport.cssPixelsToDevicePixels(this._rect.right);
        this._rect.bottom = viewport.cssPixelsToDevicePixels(this._rect.bottom);
        this._iModel = viewport.iModel;
    }
    [Symbol.iterator]() {
        function* iterator(pixels, rect, iModel) {
            for (let x = rect.left; x < rect.right; x++) {
                for (let y = rect.top; y < rect.bottom; y++) {
                    const pixel = pixels.getPixel(x, y);
                    if (pixel.feature && pixel.modelId) {
                        yield {
                            elementId: pixel.feature.elementId,
                            subCategoryId: pixel.feature.subCategoryId,
                            geometryClass: pixel.feature.geometryClass,
                            modelId: pixel.modelId,
                            iModel: pixel.iModel ?? iModel,
                        };
                    }
                }
            }
        }
        return iterator(this._pixels, this._rect, this._iModel);
    }
}
/** Implementation of [[Viewport.queryVisibleFeatures]].
 * @internal
 */
export function queryVisibleFeatures(viewport, options, callback) {
    assert("screen" === options.source || "tiles" === options.source);
    switch (options.source) {
        case "screen":
            const rect = options.rect ?? viewport.viewRect;
            viewport.readPixels(rect, Pixel.Selector.Feature, (pixels) => invokeCallback(pixels ? new ScreenFeatures(pixels, rect, viewport) : [], callback), true !== options.includeNonLocatable);
            break;
        case "tiles":
            viewport.target.queryVisibleTileFeatures(options, viewport.iModel, (features) => invokeCallback(features, callback));
            break;
        default:
            invokeCallback([], callback);
            break;
    }
}
//# sourceMappingURL=VisibleFeature.js.map