"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.EnvironmentDecorations = void 0;
const core_bentley_1 = require("@itwin/core-bentley");
const core_common_1 = require("@itwin/core-common");
const core_geometry_1 = require("@itwin/core-geometry");
const ImageUtil_1 = require("./common/ImageUtil");
const IModelApp_1 = require("./IModelApp");
const GraphicType_1 = require("./common/render/GraphicType");
/** @internal */
class EnvironmentDecorations {
    constructor(view, onLoaded, onDispose) {
        this._environment = view.displayStyle.environment;
        this._view = view;
        this._onLoaded = onLoaded;
        this._onDispose = onDispose;
        this._sky = {};
        this.loadSkyBox();
        if (this._environment.displayGround)
            this.loadGround();
    }
    dispose() {
        this._ground = undefined;
        this._sky.params = this._sky.promise = undefined;
        this._onDispose();
    }
    setEnvironment(env) {
        const prev = this._environment;
        if (prev === env)
            return;
        this._environment = env;
        // Update ground plane
        if (!env.displayGround || env.ground !== prev.ground)
            this._ground = undefined;
        if (env.displayGround && !this._ground)
            this.loadGround();
        // Update sky box
        if (env.sky !== prev.sky)
            this.loadSkyBox();
    }
    decorate(context) {
        const env = this._environment;
        let sky;
        if (env.displayAtmosphere) {
            sky = IModelApp_1.IModelApp.renderSystem.createSkyBox(this.createSkyGradientParams());
        }
        else if (env.displaySky && this._sky.params) {
            sky = IModelApp_1.IModelApp.renderSystem.createSkyBox(this._sky.params);
        }
        if (sky)
            context.setSkyBox(sky);
        if (!env.displayGround || !this._ground)
            return;
        const extents = this._view.getGroundExtents(context.viewport);
        if (extents.isNull)
            return;
        const points = [extents.low.clone(), extents.low.clone(), extents.high.clone(), extents.high.clone()];
        points[1].x = extents.high.x;
        points[3].x = extents.low.x;
        const aboveGround = this._view.isEyePointAbove(extents.low.z);
        const params = aboveGround ? this._ground.aboveParams : this._ground.belowParams;
        const builder = context.createGraphicBuilder(GraphicType_1.GraphicType.WorldDecoration);
        builder.activateGraphicParams(params);
        const strokeOptions = new core_geometry_1.StrokeOptions();
        strokeOptions.needParams = true;
        const polyfaceBuilder = core_geometry_1.PolyfaceBuilder.create(strokeOptions);
        polyfaceBuilder.toggleReversedFacetFlag();
        const uvParams = [core_geometry_1.Point2d.create(0, 0), core_geometry_1.Point2d.create(1, 0), core_geometry_1.Point2d.create(1, 1), core_geometry_1.Point2d.create(0, 1)];
        polyfaceBuilder.addQuadFacet(points, uvParams);
        const polyface = polyfaceBuilder.claimPolyface(false);
        builder.addPolyface(polyface, true);
        context.addDecorationFromBuilder(builder);
    }
    loadGround() {
        (0, core_bentley_1.assert)(undefined === this._ground);
        const aboveParams = this.createGroundParams(true);
        const belowParams = this.createGroundParams(false);
        if (aboveParams && belowParams)
            this._ground = { aboveParams, belowParams };
    }
    createGroundParams(above) {
        // Create a gradient texture.
        const ground = this._environment.ground;
        const values = [0, 0.25, 0.5];
        const color = above ? ground.aboveColor : ground.belowColor;
        const alpha = above ? 0x80 : 0x85;
        const groundColors = [color.withTransparency(0xff), color, color];
        groundColors[1] = groundColors[2] = color.withTransparency(alpha);
        const gradient = new core_common_1.Gradient.Symb();
        gradient.mode = core_common_1.Gradient.Mode.Spherical;
        gradient.keys = [{ color: groundColors[0], value: values[0] }, { color: groundColors[1], value: values[1] }, { color: groundColors[2], value: values[2] }];
        const texture = IModelApp_1.IModelApp.renderSystem.getGradientTexture(gradient, this._view.iModel);
        if (!texture)
            return undefined;
        // Create a material using the gradient texture
        const material = IModelApp_1.IModelApp.renderSystem.createRenderMaterial({
            diffuse: { color: core_common_1.ColorDef.white, weight: 0 },
            textureMapping: {
                texture,
                transform: new core_common_1.TextureMapping.Trans2x3(0, 1, 0, 1, 0, 0),
            },
        });
        if (!material)
            return undefined;
        // Create GraphicParams using the material.
        const params = new core_common_1.GraphicParams();
        params.lineColor = gradient.keys[0].color;
        params.fillColor = core_common_1.ColorDef.white; // Fill should be set to opaque white for gradient texture...
        params.material = material;
        return params;
    }
    loadSkyBox() {
        const loader = this.loadSkyBoxParams();
        if (undefined === loader.preload) {
            this.setSky(loader.load());
            return;
        }
        const promise = loader.preload;
        this._sky.promise = promise;
        loader.preload.then((loaded) => {
            if (promise === this._sky.promise)
                this.setSky(loaded ? loader.load() : undefined);
        }).catch(() => {
            if (promise === this._sky.promise)
                this.setSky(undefined);
        });
    }
    setSky(params) {
        this._sky.promise = undefined;
        this._sky.params = params ?? this.createSkyGradientParams();
        this._onLoaded();
    }
    loadSkyBoxParams() {
        let load;
        let preload;
        const sky = this._environment.sky;
        if (sky instanceof core_common_1.SkyCube) {
            const key = this.createCubeImageKey(sky);
            load = () => {
                const texture = IModelApp_1.IModelApp.renderSystem.findTexture(key, this._view.iModel);
                return texture ? { type: "cube", texture } : undefined;
            };
            if (!IModelApp_1.IModelApp.renderSystem.findTexture(key, this._view.iModel)) {
                // Some faces may use the same image. Only request each image once.
                const promises = [];
                const specs = new Set([sky.images.front, sky.images.back, sky.images.left, sky.images.right, sky.images.top, sky.images.bottom]);
                for (const spec of specs)
                    promises.push(this.imageFromSpec(spec));
                preload = Promise.all(promises).then((images) => {
                    const idToImage = new Map();
                    let index = 0;
                    for (const spec of specs) {
                        const image = images[index++];
                        if (!image)
                            return false;
                        else
                            idToImage.set(spec, image);
                    }
                    // eslint-disable-next-line deprecation/deprecation
                    const params = new core_common_1.RenderTexture.Params(key, core_common_1.RenderTexture.Type.SkyBox);
                    const txImgs = [
                        idToImage.get(sky.images.front), idToImage.get(sky.images.back), idToImage.get(sky.images.top),
                        idToImage.get(sky.images.bottom), idToImage.get(sky.images.right), idToImage.get(sky.images.left),
                    ];
                    return undefined !== IModelApp_1.IModelApp.renderSystem.createTextureFromCubeImages(txImgs[0], txImgs[1], txImgs[2], txImgs[3], txImgs[4], txImgs[5], this._view.iModel, params);
                });
            }
        }
        else if (sky instanceof core_common_1.SkySphere) {
            load = () => {
                const texture = IModelApp_1.IModelApp.renderSystem.findTexture(sky.image, this._view.iModel);
                return texture ? {
                    type: "sphere",
                    texture,
                    rotation: 0,
                    zOffset: this._view.iModel.globalOrigin.z,
                } : undefined;
            };
            if (!IModelApp_1.IModelApp.renderSystem.findTexture(sky.image, this._view.iModel)) {
                preload = this.imageFromSpec(sky.image).then((image) => {
                    if (!image)
                        return false;
                    return undefined !== IModelApp_1.IModelApp.renderSystem.createTexture({
                        image: { source: image },
                        ownership: { iModel: this._view.iModel, key: sky.image },
                    });
                });
            }
        }
        else {
            load = () => this.createSkyGradientParams();
        }
        return { load, preload };
    }
    createCubeImageKey(sky) {
        const i = sky.images;
        return `skycube:${i.front}:${i.back}:${i.left}:${i.right}:${i.top}:${i.bottom}`;
    }
    createSkyGradientParams() {
        return {
            type: "gradient",
            gradient: this._environment.sky.gradient,
            zOffset: this._view.iModel.globalOrigin.z,
        };
    }
    async imageFromSpec(spec) {
        if (core_bentley_1.Id64.isValidId64(spec))
            return (await IModelApp_1.IModelApp.renderSystem.loadTextureImage(spec, this._view.iModel))?.image;
        return (0, ImageUtil_1.tryImageElementFromUrl)(spec);
    }
}
exports.EnvironmentDecorations = EnvironmentDecorations;
//# sourceMappingURL=EnvironmentDecorations.js.map