/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module WebGL
 */
import { assert, dispose } from "@itwin/core-bentley";
import { Gradient, RenderTexture, ThematicDisplayMode, ThematicGradientMode, ThematicGradientTransparencyMode, } from "@itwin/core-common";
import { TextureUnit } from "./RenderFlags";
import { desync, sync } from "./Sync";
import { TextureHandle } from "./Texture";
import { ThematicSensors } from "./ThematicSensors";
import { Angle, Range3d, Vector3d } from "@itwin/core-geometry";
import { System } from "./System";
import { FloatRgba } from "./FloatRGBA";
/** Maintains state for uniforms related to thematic display.
 * @internal
 */
export class ThematicUniforms {
    _sensors; // NB: This is only used if no distance cutoff is applied (this is shared among all batches)
    _texture;
    _range = new Float32Array(2);
    _colorMix = 0.0;
    _axis = new Float32Array(3);
    _sunDirection = new Float32Array(3);
    _marginColor = new FloatRgba();
    _displayMode = new Float32Array(1);
    _fragSettings = new Float32Array(4); // gradientMode, distanceCutoff, stepCount, > 0.0 if multiply gradient alpha
    _numSensors = 0;
    _gradientDimension = _getGradientDimension();
    _thematicDisplay;
    syncKey = 0;
    get _distanceCutoff() { return this._fragSettings[1]; }
    get thematicDisplay() {
        return this._thematicDisplay;
    }
    get wantIsoLines() {
        if (undefined !== this.thematicDisplay)
            return ThematicDisplayMode.Height === this._displayMode[0] && ThematicGradientMode.IsoLines === this.thematicDisplay.gradientSettings.mode;
        return false;
    }
    get wantSlopeMode() {
        return (undefined !== this.thematicDisplay) ? ThematicDisplayMode.Slope === this._displayMode[0] : false;
    }
    get wantHillShadeMode() {
        return (undefined !== this.thematicDisplay) ? ThematicDisplayMode.HillShade === this._displayMode[0] : false;
    }
    get wantGlobalSensorTexture() {
        return !(this._distanceCutoff > 0);
    }
    get bytesUsed() {
        return this._sensors ? this._sensors.bytesUsed : 0;
    }
    _scratchVector = new Vector3d();
    _updateAxis(axis, viewMatrix) {
        const tAxis = (viewMatrix !== undefined) ? viewMatrix.multiplyVector(axis, this._scratchVector) : axis;
        tAxis.normalizeInPlace();
        this._axis[0] = tAxis.x;
        this._axis[1] = tAxis.y;
        this._axis[2] = tAxis.z;
    }
    _updateSunDirection(sunDir, viewMatrix) {
        viewMatrix.multiplyVector(sunDir, this._scratchVector);
        this._scratchVector.negate(this._scratchVector);
        this._scratchVector.normalizeInPlace();
        this._sunDirection[0] = this._scratchVector.x;
        this._sunDirection[1] = this._scratchVector.y;
        this._sunDirection[2] = this._scratchVector.z;
    }
    update(target) {
        const plan = target.plan;
        if (this.thematicDisplay && plan.thematic && this.thematicDisplay.equals(plan.thematic) && this._texture) {
            if (undefined !== this._sensors)
                this._sensors.update(target.uniforms.frustum.viewMatrix);
            if (ThematicDisplayMode.Slope === this.thematicDisplay.displayMode) {
                this._updateAxis(this.thematicDisplay.axis, target.uniforms.frustum.viewMatrix);
                desync(this);
            }
            else if (ThematicDisplayMode.HillShade === this.thematicDisplay.displayMode) {
                this._updateSunDirection(this.thematicDisplay.sunDirection, target.uniforms.frustum.viewMatrix);
                desync(this);
            }
            return;
        }
        desync(this);
        this._thematicDisplay = plan.thematic;
        this._texture = dispose(this._texture);
        if (!this.thematicDisplay)
            return;
        if (ThematicDisplayMode.Slope === this.thematicDisplay.displayMode) {
            this._range[0] = Angle.degreesToRadians(this.thematicDisplay.range.low);
            this._range[1] = Angle.degreesToRadians(this.thematicDisplay.range.high);
        }
        else {
            this._range[0] = this.thematicDisplay.range.low;
            this._range[1] = this.thematicDisplay.range.high;
        }
        this._colorMix = this.thematicDisplay.gradientSettings.colorMix;
        this._updateAxis(this.thematicDisplay.axis, (ThematicDisplayMode.Slope === this.thematicDisplay.displayMode) ? target.uniforms.frustum.viewMatrix : undefined);
        if (ThematicDisplayMode.HillShade === this.thematicDisplay.displayMode)
            this._updateSunDirection(this.thematicDisplay.sunDirection, target.uniforms.frustum.viewMatrix);
        this._marginColor.setColorDef(this.thematicDisplay.gradientSettings.marginColor);
        this._displayMode[0] = this.thematicDisplay.displayMode;
        this._fragSettings[0] = this.thematicDisplay.gradientSettings.mode;
        const sensorSettings = this.thematicDisplay.sensorSettings;
        this._fragSettings[1] = (undefined === sensorSettings) ? 0 : this.thematicDisplay.sensorSettings.distanceCutoff;
        this._fragSettings[2] = Math.min(this.thematicDisplay.gradientSettings.stepCount, this._gradientDimension);
        this._fragSettings[3] = this.thematicDisplay.gradientSettings.transparencyMode === ThematicGradientTransparencyMode.SurfaceOnly ? 0.0 : 1.0;
        // If we want sensors and have no distance cutoff, then create a global shared sensor texture.
        if (target.wantThematicSensors && !(this._distanceCutoff > 0)) {
            this._numSensors = sensorSettings.sensors.length;
            this._sensors = dispose(this._sensors);
            this._sensors = ThematicSensors.create(target, Range3d.createNull());
        }
        const symb = Gradient.Symb.createThematic(this.thematicDisplay.gradientSettings);
        const image = symb.getThematicImageForRenderer(this._gradientDimension);
        this._texture = TextureHandle.createForImageBuffer(image, RenderTexture.Type.ThematicGradient);
    }
    bindRange(uniform) {
        if (!sync(this, uniform))
            uniform.setUniform2fv(this._range);
    }
    bindAxis(uniform) {
        if (!sync(this, uniform))
            uniform.setUniform3fv(this._axis);
    }
    bindSunDirection(uniform) {
        if (!sync(this, uniform))
            uniform.setUniform3fv(this._sunDirection);
    }
    bindMarginColor(uniform) {
        if (!sync(this, uniform))
            this._marginColor.bind(uniform);
    }
    bindDisplayMode(uniform) {
        if (!sync(this, uniform))
            uniform.setUniform1fv(this._displayMode);
    }
    bindFragSettings(uniform) {
        if (!sync(this, uniform))
            uniform.setUniform4fv(this._fragSettings);
    }
    bindTexture(uniform, unit) {
        assert(undefined !== this._texture);
        this._texture.bindSampler(uniform, unit);
    }
    bindNumSensors(uniform) {
        if (!sync(this, uniform))
            uniform.setUniform1i(this._numSensors);
    }
    bindSensors(uniform) {
        assert(undefined !== this._sensors);
        this._sensors.texture.bindSampler(uniform, TextureUnit.ThematicSensors);
    }
    get isDisposed() {
        return undefined === this._texture && undefined === this._sensors;
    }
    [Symbol.dispose]() {
        this._texture = dispose(this._texture);
        this._sensors = dispose(this._sensors);
    }
}
function _getGradientDimension() {
    const preferDimension = 8192;
    const maxDimension = System.instance.maxTextureSize;
    return (preferDimension > maxDimension) ? maxDimension : preferDimension;
}
//# sourceMappingURL=ThematicUniforms.js.map