"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.SheetViewState = void 0;
const core_bentley_1 = require("@itwin/core-bentley");
const core_geometry_1 = require("@itwin/core-geometry");
const core_common_1 = require("@itwin/core-common");
const CategorySelectorState_1 = require("./CategorySelectorState");
const DisplayStyleState_1 = require("./DisplayStyleState");
const Frustum2d_1 = require("./Frustum2d");
const ViewState_1 = require("./ViewState");
const GraphicType_1 = require("./common/render/GraphicType");
const SheetViewAttachments_1 = require("./internal/SheetViewAttachments");
// cSpell:ignore ovrs
/** Describes the geometry and styling of a sheet border decoration.
 * The sheet border decoration mimics a sheet of paper with a drop shadow.
 */
class SheetBorder {
    _rect;
    _shadow;
    _gradient;
    constructor(rect, shadow, gradient) {
        this._rect = rect;
        this._shadow = shadow;
        this._gradient = gradient;
    }
    /** Create a new sheet border. If a context is supplied, points are transformed to view coordinates. */
    static create(width, height, context) {
        // Rect
        const rect = [
            core_geometry_1.Point3d.create(0, height),
            core_geometry_1.Point3d.create(0, 0),
            core_geometry_1.Point3d.create(width, 0),
            core_geometry_1.Point3d.create(width, height),
            core_geometry_1.Point3d.create(0, height)
        ];
        if (context) {
            context.viewport.worldToViewArray(rect);
        }
        // Shadow
        const shadowWidth = .01 * Math.sqrt(width * width + height * height);
        const shadow = [
            core_geometry_1.Point3d.create(shadowWidth, 0),
            core_geometry_1.Point3d.create(shadowWidth, -shadowWidth),
            core_geometry_1.Point3d.create(width + shadowWidth, -shadowWidth),
            core_geometry_1.Point3d.create(width + shadowWidth, height - shadowWidth),
            core_geometry_1.Point3d.create(width, height - shadowWidth),
            core_geometry_1.Point3d.create(width, 0),
            core_geometry_1.Point3d.create(shadowWidth, 0),
        ];
        if (context) {
            context.viewport.worldToViewArray(shadow);
        }
        // Gradient
        const gradient = new core_common_1.Gradient.Symb();
        gradient.mode = core_common_1.Gradient.Mode.Linear;
        gradient.angle = core_geometry_1.Angle.createDegrees(-45);
        gradient.keys = [{ value: 0, color: core_common_1.ColorDef.from(25, 25, 25) }, { value: 0.5, color: core_common_1.ColorDef.from(150, 150, 150) }];
        // Copy over points
        const rect2d = [];
        for (const point of rect)
            rect2d.push(core_geometry_1.Point2d.createFrom(point));
        const shadow2d = [];
        for (const point of shadow)
            shadow2d.push(core_geometry_1.Point2d.createFrom(point));
        return new SheetBorder(rect2d, shadow2d, gradient);
    }
    getRange() {
        const range = core_geometry_1.Range2d.createArray(this._rect);
        const shadowRange = core_geometry_1.Range2d.createArray(this._shadow);
        range.extendRange(shadowRange);
        return range;
    }
    /** Add this border to the given GraphicBuilder. */
    addToBuilder(builder) {
        const lineColor = core_common_1.ColorDef.black;
        const fillColor = core_common_1.ColorDef.black;
        const params = new core_common_1.GraphicParams();
        params.fillColor = fillColor;
        params.gradient = this._gradient;
        builder.activateGraphicParams(params);
        builder.addShape2d(this._shadow, Frustum2d_1.Frustum2d.minimumZDistance);
        builder.setSymbology(lineColor, fillColor, 2);
        builder.addLineString2d(this._rect, 0);
    }
}
/** A view of a [SheetModel]($backend).
 * @public
 * @extensions
 */
class SheetViewState extends ViewState_1.ViewState2d {
    /** The width and height of the sheet in world coordinates. */
    sheetSize;
    _viewAttachments;
    _viewedExtents;
    _onViewAttachmentsReloaded = () => undefined;
    /** Strictly for tests. */
    onViewAttachmentsReloaded = new core_bentley_1.BeEvent();
    get attachmentIds() {
        return this._viewAttachments.attachmentIds;
    }
    static get className() { return "SheetViewDefinition"; }
    static createFromProps(viewStateData, iModel) {
        const cat = new CategorySelectorState_1.CategorySelectorState(viewStateData.categorySelectorProps, iModel);
        const displayStyleState = new DisplayStyleState_1.DisplayStyle2dState(viewStateData.displayStyleProps, iModel);
        // use "new this" so subclasses are correct
        return new this(viewStateData.viewDefinitionProps, iModel, cat, displayStyleState, (0, core_bentley_1.expectDefined)(viewStateData.sheetProps), (0, core_bentley_1.expectDefined)(viewStateData.sheetAttachments));
    }
    toProps() {
        const props = super.toProps();
        props.sheetAttachments = [...this.attachmentIds];
        // For sheetProps all that is actually used is the size, so just null out everything else.
        const codeProps = { spec: "", scope: "", value: "" };
        props.sheetProps = {
            model: "",
            code: codeProps,
            classFullName: "",
            width: this.sheetSize.x,
            height: this.sheetSize.y,
            scale: 1,
        };
        return props;
    }
    /** Strictly for testing. @internal */
    get viewAttachmentProps() {
        return this._viewAttachments.attachmentProps;
    }
    /** Strictly for testing. @internal */
    get viewAttachmentInfos() {
        return this._viewAttachments.attachmentInfos;
    }
    /** Strictly for testing. @internal */
    get attachments() {
        return this._viewAttachments.attachments;
    }
    isDrawingView() { return false; }
    isSheetView() { return true; }
    constructor(props, iModel, categories, displayStyle, sheetProps, attachments) {
        super(props, iModel, categories, displayStyle);
        if (categories instanceof SheetViewState) {
            // we are coming from clone...
            this.sheetSize = categories.sheetSize.clone();
            this._viewAttachments = categories._viewAttachments.clone(iModel);
            this._viewedExtents = categories._viewedExtents.clone();
        }
        else {
            this.sheetSize = core_geometry_1.Point2d.create(sheetProps.width, sheetProps.height);
            this._viewAttachments = SheetViewAttachments_1.SheetViewAttachments.create(attachments);
            const extents = new core_geometry_1.Range3d(0, 0, 0, this.sheetSize.x, this.sheetSize.y, 0);
            const margin = 1.1;
            extents.scaleAboutCenterInPlace(margin);
            this._viewedExtents = extents;
        }
        if (iModel.isBriefcaseConnection()) {
            iModel.txns.onElementsChanged.addListener(async (changes) => {
                let reload = false;
                for (const change of changes.filter({ includeMetadata: (meta) => meta.is("BisCore:ViewAttachment") })) {
                    if (change.type === "inserted" || this._viewAttachments.attachmentIds.includes(change.id)) {
                        reload = true;
                        break;
                    }
                }
                if (reload) {
                    if (await this._viewAttachments.reload(this.baseModelId, iModel)) {
                        this._onViewAttachmentsReloaded();
                        this.onViewAttachmentsReloaded.raiseEvent();
                    }
                }
            });
        }
    }
    getOrigin() {
        const origin = super.getOrigin();
        origin.z = -this._viewAttachments.maxDepth;
        return origin;
    }
    getExtents() {
        const extents = super.getExtents();
        extents.z = this._viewAttachments.maxDepth + Frustum2d_1.Frustum2d.minimumZDistance;
        return extents;
    }
    /** Overrides [[ViewState.discloseTileTrees]] to include tile trees associated with [ViewAttachment]($backend)s displayed on this sheet. */
    discloseTileTrees(trees) {
        super.discloseTileTrees(trees);
        trees.disclose(this._viewAttachments);
    }
    /** @internal */
    collectNonTileTreeStatistics(stats) {
        super.collectNonTileTreeStatistics(stats);
        this._viewAttachments.collectStatistics(stats);
    }
    get defaultExtentLimits() {
        return { min: core_geometry_1.Constant.oneMillimeter, max: this.sheetSize.magnitude() * 10 };
    }
    getViewedExtents() {
        return this._viewedExtents;
    }
    /** @internal */
    preload(hydrateRequest) {
        super.preload(hydrateRequest);
        this._viewAttachments.preload(hydrateRequest);
    }
    /** @internal */
    async postload(hydrateResponse) {
        const promises = [];
        promises.push(super.postload(hydrateResponse));
        promises.push(this._viewAttachments.postload(hydrateResponse, this.iModel));
        await Promise.all(promises);
    }
    /** @internal */
    createScene(context) {
        super.createScene(context);
        this._viewAttachments.addToScene(context);
    }
    /** @internal */
    get secondaryViewports() {
        return this._viewAttachments.getSecondaryViewports();
    }
    /** @internal */
    async queryAttachmentIds() {
        const ecsql = `SELECT ECInstanceId as attachmentId FROM bis.ViewAttachment WHERE model.Id=${this.baseModelId}`;
        const ids = [];
        for await (const row of this.iModel.createQueryReader(ecsql))
            ids.push(row[0]);
        return ids;
    }
    async changeViewedModel(modelId) {
        await super.changeViewedModel(modelId);
        const attachmentIds = await this.queryAttachmentIds();
        (0, core_bentley_1.dispose)(this._viewAttachments);
        this._viewAttachments = SheetViewAttachments_1.SheetViewAttachments.create(attachmentIds);
    }
    /** See [[ViewState.attachToViewport]]. */
    attachToViewport(args) {
        super.attachToViewport(args);
        this._viewAttachments.attachToViewport({
            backgroundColor: this.displayStyle.backgroundColor,
            sheetModelId: this.baseModelId,
        });
        this._onViewAttachmentsReloaded = () => args.invalidateController();
    }
    /** See [[ViewState.detachFromViewport]]. */
    detachFromViewport() {
        super.detachFromViewport();
        this._viewAttachments.detachFromViewport();
        this._onViewAttachmentsReloaded = () => undefined;
    }
    get areAllTileTreesLoaded() {
        if (!super.areAllTileTreesLoaded) {
            return false;
        }
        let displayedExtents = this._viewedExtents;
        const frustum = this.calculateFrustum();
        if (frustum) {
            displayedExtents = frustum.toRange();
        }
        return this._viewAttachments.areAllTileTreesLoaded(displayedExtents);
    }
    /** @internal Strictly for testing */
    areAllAttachmentsLoaded() {
        return this._viewAttachments.areAllAttachmentsLoaded();
    }
    /** Create a sheet border decoration graphic. */
    createBorder(width, height, context) {
        const border = SheetBorder.create(width, height, context);
        const builder = context.createGraphicBuilder(GraphicType_1.GraphicType.ViewBackground);
        border.addToBuilder(builder);
        return builder.finish();
    }
    /** @internal */
    decorate(context) {
        super.decorate(context);
        if (this.sheetSize !== undefined) {
            const border = this.createBorder(this.sheetSize.x, this.sheetSize.y, context);
            context.setViewBackground(border);
        }
    }
    computeFitRange() {
        const size = this.sheetSize;
        if (0 >= size.x || 0 >= size.y)
            return super.computeFitRange();
        return new core_geometry_1.Range3d(0, 0, -1, size.x, size.y, 1);
    }
    /** @internal */
    getAttachmentViewport(args) {
        return this._viewAttachments.getAttachmentViewport(args);
    }
    /** @beta */
    computeDisplayTransform(args) {
        // ###TODO we're currently ignoring model and element Id in args, assuming irrelevant for sheets.
        // Should probably call super or have super call us.
        return this._viewAttachments.computeDisplayTransform(args);
    }
}
exports.SheetViewState = SheetViewState;
//# sourceMappingURL=SheetViewState.js.map