"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Views
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.SheetViewAttachments = void 0;
const core_common_1 = require("@itwin/core-common");
const core_bentley_1 = require("@itwin/core-bentley");
const IModelApp_1 = require("../IModelApp");
const ViewAttachmentRenderer_1 = require("./ViewAttachmentRenderer");
const Frustum2d_1 = require("../Frustum2d");
/** Stateless state for a sheet that has no view attachments. */
class EmptyAttachments {
    // We only need one instance of this stateless class.
    static _instance;
    static get() {
        return this._instance ?? (this._instance = new EmptyAttachments());
    }
    constructor() { }
    clone() {
        return this;
    }
    preload() { }
    async postload() {
        return this;
    }
    get attachmentIds() {
        return [];
    }
}
/** Holds the element Ids of the view attachments to be loaded for display. */
class AttachmentIds {
    _ids;
    constructor(ids) {
        this._ids = ids;
    }
    get attachmentIds() {
        return this._ids;
    }
    clone() {
        return new AttachmentIds([...this._ids]);
    }
    preload(request) {
        request.sheetViewAttachmentIds = core_bentley_1.CompressedId64Set.sortAndCompress(this._ids);
        request.viewStateLoadProps = {
            displayStyle: {
                omitScheduleScriptElementIds: !IModelApp_1.IModelApp.tileAdmin.enableFrontendScheduleScripts,
                compressExcludedElementIds: true,
            },
        };
    }
    async postload(response, iModel) {
        if (undefined === response.sheetViewViews || undefined === response.sheetViewAttachmentProps) {
            return this;
        }
        const viewStateProps = response.sheetViewViews;
        const promises = [];
        for (const viewProps of viewStateProps) {
            const loadView = async () => {
                try {
                    if (viewProps === undefined)
                        return undefined;
                    const view = await iModel.views.convertViewStatePropsToViewState(viewProps);
                    return view;
                }
                catch {
                    return undefined;
                }
            };
            promises.push(loadView());
        }
        const views = await Promise.all(promises);
        const attachmentProps = response.sheetViewAttachmentProps;
        (0, core_bentley_1.assert)(views.length === attachmentProps.length);
        const infos = [];
        for (let i = 0; i < views.length; i++) {
            const view = views[i];
            if (view && !(view.isSheetView())) {
                const props = attachmentProps[i];
                props.attachedView = view;
                infos.push(props);
            }
        }
        return new AttachmentInfos(infos);
    }
}
/** Fully loaded view attachments. */
class AttachmentInfos {
    infos;
    constructor(infos) {
        this.infos = infos;
    }
    get attachmentIds() {
        return this.infos.map((x) => (0, core_bentley_1.expectDefined)(x.id));
    }
    clone(iModel) {
        const infos = this.infos.map((info) => {
            return {
                ...info,
                attachedView: info.attachedView.clone(iModel),
            };
        });
        return new AttachmentInfos(infos);
    }
    preload() {
        // already loaded.
    }
    async postload() {
        // already loaded.
        return this;
    }
}
/** Reloads the attachments after a change to the database. */
async function reloadAttachments(sheetModelId, iModel) {
    const ecsql = `SELECT ECInstanceId as attachmentId FROM bis.ViewAttachment WHERE model.Id=${sheetModelId}`;
    const ids = [];
    for await (const row of iModel.createQueryReader(ecsql)) {
        ids.push(row[0]);
    }
    const attachmentProps = await iModel.elements.getProps(ids);
    const promises = [];
    for (const attachment of attachmentProps) {
        const loadView = async () => {
            try {
                const view = await iModel.views.load(attachment.view.id);
                return view;
            }
            catch {
                return undefined;
            }
        };
        promises.push(loadView());
    }
    const views = await Promise.all(promises);
    (0, core_bentley_1.assert)(views.length === attachmentProps.length);
    const infos = [];
    for (let i = 0; i < views.length; i++) {
        const view = views[i];
        if (view && !view.isSheetView()) {
            const props = attachmentProps[i];
            props.attachedView = view;
            infos.push(props);
        }
    }
    return new AttachmentInfos(infos);
}
function disposeRenderers(renderers) {
    if (renderers) {
        for (const renderer of renderers) {
            renderer[Symbol.dispose]();
        }
    }
}
/** Manages the set of ViewAttachment elements to be rendered by a SheetViewState.
 * Takes care of reloading them after ViewAttachment elements are modified, deleted, or inserted.
 */
class SheetViewAttachments {
    _impl;
    _reload;
    _maxDepth = Frustum2d_1.Frustum2d.minimumZDistance;
    _rendererArgs;
    _renderers;
    get maxDepth() {
        return this._maxDepth;
    }
    *getSecondaryViewports() {
        if (this._renderers) {
            for (const renderer of this._renderers) {
                if (renderer.viewport) {
                    yield renderer.viewport;
                }
            }
        }
    }
    constructor(impl) {
        this._impl = impl;
    }
    [Symbol.dispose]() {
        disposeRenderers(this._renderers);
        this._renderers = this._rendererArgs = undefined;
        this._reload = undefined;
    }
    static create(attachmentIds) {
        const impl = attachmentIds.length === 0 ? EmptyAttachments.get() : new AttachmentIds([...attachmentIds]);
        return new this(impl);
    }
    get attachmentIds() {
        return this._impl.attachmentIds;
    }
    clone(iModel) {
        return new SheetViewAttachments(this._impl.clone(iModel));
    }
    preload(request) {
        this._impl.preload(request);
    }
    async postload(response, iModel) {
        this._impl = await this._impl.postload(response, iModel);
    }
    async reload(sheetModelId, iModel) {
        const renderers = this._renderers;
        const reload = this._reload = reloadAttachments(sheetModelId, iModel);
        const impl = await this._reload;
        // We keep the previous renderers until reloading completes, to avoid drawing a blank view while waiting.
        // Afterward, always destroy the previous renderers.
        disposeRenderers(renderers);
        // If reload was not called again while we waited...
        if (this._reload === reload) {
            this._impl = impl;
            this._reload = this._renderers = undefined;
            if (this._rendererArgs) {
                // We are attached to a Viewport - reload the renderers.
                this.loadRenderers();
            }
            return true;
        }
        return false;
    }
    attachToViewport(args) {
        (0, core_bentley_1.assert)(undefined === this._renderers);
        (0, core_bentley_1.assert)(undefined === this._rendererArgs);
        this._rendererArgs = args;
        this.loadRenderers();
    }
    detachFromViewport() {
        (0, core_bentley_1.assert)(undefined !== this._rendererArgs);
        this._rendererArgs = undefined;
        disposeRenderers(this._renderers);
        this._renderers = undefined;
    }
    areAllTileTreesLoaded(displayedExtents) {
        if (this._reload) {
            return false;
        }
        else if (!this._renderers) {
            return true;
        }
        return this._renderers.every((renderer) => {
            const attachmentRange = core_common_1.Placement2d.fromJSON(renderer.viewAttachmentProps.placement).calculateRange();
            return !attachmentRange.intersectsRangeXY(displayedExtents) || renderer.areAllTileTreesLoaded;
        });
    }
    discloseTileTrees(trees) {
        for (const renderer of this.renderers()) {
            trees.disclose(renderer);
        }
    }
    collectStatistics(stats) {
        for (const renderer of this.renderers()) {
            renderer.collectStatistics(stats);
        }
    }
    addToScene(context) {
        for (const renderer of this.renderers()) {
            renderer.addToScene(context);
        }
    }
    getAttachmentViewport(args) {
        const renderer = args.viewAttachmentId ? this.findRendererById(args.viewAttachmentId) : undefined;
        if (!renderer) {
            return undefined;
        }
        return args.inSectionDrawingAttachment ? renderer.viewport?.view.getAttachmentViewport({ inSectionDrawingAttachment: true }) : renderer.viewport;
    }
    computeDisplayTransform(args) {
        const renderer = undefined !== args.viewAttachmentId ? this.findRendererById(args.viewAttachmentId) : undefined;
        const ortho = renderer?.ortho;
        const sheetTransform = ortho?.toSheet;
        if (!sheetTransform) {
            return undefined;
        }
        const sectionTransform = args.inSectionDrawingAttachment ? ortho.view.computeDisplayTransform(args) : undefined;
        if (!sectionTransform) {
            return sheetTransform.clone(args.output);
        }
        return sheetTransform.multiplyTransformTransform(sectionTransform, args.output);
    }
    /** Strictly for tests. */
    areAllAttachmentsLoaded() {
        return !this._reload && (!this._renderers || this._renderers.every((x) => x.areAllTileTreesLoaded));
    }
    /** Strictly for tests. */
    get attachments() {
        return this._renderers;
    }
    /** Strictly for tests. */
    get attachmentProps() {
        const infos = this._impl.infos;
        if (!infos) {
            return [];
        }
        return infos.map((x) => {
            return {
                ...x,
                attachedView: undefined,
            };
        });
    }
    /** Strictly for tests. */
    get attachmentInfos() {
        const infos = this._impl.infos;
        return infos ?? this._impl.attachmentIds;
    }
    loadRenderers() {
        const args = this._rendererArgs;
        (0, core_bentley_1.assert)(undefined !== args);
        (0, core_bentley_1.assert)(undefined === this._renderers);
        this._maxDepth = Frustum2d_1.Frustum2d.minimumZDistance;
        const infos = this._impl.infos;
        if (!infos) {
            return;
        }
        this._renderers = infos.map((info) => {
            const renderer = (0, ViewAttachmentRenderer_1.createViewAttachmentRenderer)({
                ...args,
                props: info,
                view: info.attachedView,
            });
            this._maxDepth = Math.max(this._maxDepth, renderer.zDepth);
            return renderer;
        });
    }
    *renderers() {
        if (this._renderers) {
            for (const renderer of this._renderers) {
                yield renderer;
            }
        }
    }
    findRendererById(id) {
        return this._renderers?.find((x) => x.viewAttachmentProps.id === id);
    }
}
exports.SheetViewAttachments = SheetViewAttachments;
//# sourceMappingURL=SheetViewAttachments.js.map