import { makePropertyChangeObservable } from '../PropertyChangeObservable';
import { ClipPlane } from './ClipPlane';
import { DitheringTextureMode } from './CustomBabylonMaterials/DitherTexture';
import { IfcFragmentHsvColoringSettings } from './IfcFragmentHsvColoringSettings';
import { Color3, DirectionalLight, HemisphericLight, Vector3 } from './babylonjs-import';
import { TransparencyMode } from './twinfinity-viewer';
import { IfcMaterialHighlightIndex } from './CustomBabylonMaterials/IfcMaterial';
import { HIGHLIGHT_COLORS } from './ShaderConstants';
import { copyVertex3ToRef } from '../math';
/**
 * Transparency settings for IFC objects.
 */
export class IfcTransparencySettings {
    /**
     * Changes how transparency is handled for transparent IFC objects
     * @param transparencyMode The type of rendering that will be used for all transparent IFC objects, dithering means that pixels are
     * rendered opaque but skipping every few pixels in a screen pattern depending on the opacity
     * Seethrough is normal alpha blending
     * AnimatedFrames changes the noise from every frame in a loop, when the camera moves, to fight a visual swimming effect when the camera moves, set to 1 to dis
     */
    mode = TransparencyMode.Seethrough;
    /**
     * @param ditheringMode What dithering mode to use, bayer uses a bayer matrix to determine which pixels to render opaque, blue noise uses a blue noise texture
     * @param animatedFrames How many frames to use for the dithering animation when moving the camera, if 1 then no animation is used. Works best with Blue Noise
     * @param ditherDiscardDepth Wheter or not to discard fragments in the depth shader that are dithered, this is needed for transparent objects to not write to the depth buffer
     */
    ditheringOptions = {
        animatedFrames: 1,
        ditherDiscardDepth: false,
        mode: DitheringTextureMode.blueNoise,
        distanceFade: false
    };
}
const initialPlanes = {
    clipPlane: { color: new Color3(1.0, 0.0, 0.0) },
    clipPlane2: { color: new Color3(0.0, 1.0, 0.0) },
    clipPlane3: { color: new Color3(0.0, 0.0, 1.0) },
    clipPlane4: { color: new Color3(1.0, 0.0, 1.0) },
    clipPlane5: { color: new Color3(0.0, 1.0, 1.0) },
    clipPlane6: { color: new Color3(1.0, 0.0, 1.0) }
};
/**
 * Represents the visual settings for an IFC model.
 */
export class IfcVisualSettings {
    lighting;
    static _temp = {
        vectorA: Vector3.Zero()
    };
    color = makePropertyChangeObservable(new IfcFragmentHsvColoringSettings(false));
    transparency;
    clipPlanes = ClipPlane.create(initialPlanes);
    _highlightColors = {
        [IfcMaterialHighlightIndex.One]: Color3.Blue(),
        [IfcMaterialHighlightIndex.Two]: Color3.Green(),
        [IfcMaterialHighlightIndex.Three]: Color3.Red()
    };
    // new Map<IfcMaterialHighlightIndex, Color3>();
    /**
     * Creates an instance of {@link IfcVisualSettings}.
     * @param lighting The lighting environment for the model.
     * @param viewer The {@link TwinfinityViewer} instance.
     */
    constructor(lighting, viewer) {
        this.lighting = lighting;
        this.transparency = makePropertyChangeObservable(new IfcTransparencySettings());
    }
    setHighlightColor(color, highlightIndex) {
        this._highlightColors[highlightIndex] = color;
    }
    /**
     * Assigns effect values for the highlight colors. For internal use only.
     * @hidden
     * @internal
     * @param effect - The effect to assign values to.
     */
    _setEffectValues(effect) {
        const enums = [
            IfcMaterialHighlightIndex.One,
            IfcMaterialHighlightIndex.Two,
            IfcMaterialHighlightIndex.Three
        ];
        for (let i = 0; i < 3; i++) {
            const color = this._highlightColors[enums[i]];
            effect.setColor3(HIGHLIGHT_COLORS + '[' + i + ']', color);
        }
    }
    setClipPlane(clipPlaneOptions) {
        const clipPlane = this.clipPlanes[clipPlaneOptions.clipPlaneIndex];
        if (clipPlane) {
            const enabled = clipPlaneOptions.enabled;
            clipPlane.enabled = enabled;
            if (enabled) {
                if (clipPlaneOptions.ignoreDepthWrite !== undefined &&
                    clipPlaneOptions.ignoreDepthWrite !== clipPlane.ignoreDepthWrite) {
                    clipPlane.ignoreDepthWrite = clipPlaneOptions.ignoreDepthWrite;
                }
                const fadeSize = clipPlaneOptions.fadeSize;
                if (fadeSize !== undefined) {
                    if (fadeSize < 0.0) {
                        throw new Error('FadeSize must be equal to or larger than 0');
                    }
                }
                const point = clipPlaneOptions.point;
                const normalVector = copyVertex3ToRef(clipPlaneOptions.normalVector, IfcVisualSettings._temp.vectorA).normalize();
                const color = clipPlaneOptions.color;
                if (normalVector.lengthSquared() > 0.0) {
                    copyVertex3ToRef(clipPlane.position, point);
                    copyVertex3ToRef(normalVector, clipPlane.normal);
                }
                else {
                    throw new Error('Normal not set');
                }
                if (color) {
                    clipPlane.color.r = color.r;
                    clipPlane.color.g = color.g;
                    clipPlane.color.b = color.b;
                }
                if (fadeSize !== undefined) {
                    clipPlane.fadeSize = fadeSize;
                }
                const nX = clipPlane.normal.x;
                const nY = clipPlane.normal.y;
                const nZ = clipPlane.normal.z;
                const pX = clipPlane.position.x;
                const pY = clipPlane.position.y;
                const pZ = clipPlane.position.z;
                const normalVec3 = new Vector3(nX, nY, nZ);
                const pointVec3 = new Vector3(pX, pY, pZ);
                const d = Vector3.Dot(normalVec3, pointVec3);
                clipPlane.plane.normal.set(nX, nY, nZ);
                clipPlane.plane.d = -d;
            }
        }
        else {
            throw new Error('Unknown clipplane index');
        }
    }
}
/**
 * Visual settings for {@link TwinfinityViewer}. Controls things like lighting, IFC object transparency etc
 */
export class VisualSettings {
    _lighting;
    /**
     * Visual settings for IFC objects. These settings are applied to all IFC objects in the scene.
     */
    ifc;
    // TODO: If we have dwg specific settings, we should add them here.
    /**
     * Constructor for the visual settings.
     * @param viewer {@link TwinfinityViewer} instance this {@link VisualSettings} instance is associated with.
     */
    constructor(viewer) {
        const directionalLightDirection = new Vector3(0.0, -1.0, 0.25).normalize();
        const directionalLight = new DirectionalLight('default directional light', directionalLightDirection, viewer.scene);
        this._lighting = {
            directionalLight: directionalLight,
            hemisphericLight: new HemisphericLight('default hemispheric light', Vector3.Up(), viewer.scene),
            ambient: new Color3(0.5, 0.5, 0.5),
            hemisphericLightStrength: 0.5,
            directionalLightStrength: 0.35,
            directionalLightLambertStrength: 1.0,
            directionalLightSpecularStrength: 0.5
        };
        this._lighting.hemisphericLight.diffuse.set(0.85, 0.85, 0.85);
        this._lighting.hemisphericLight.groundColor = new Color3(0.45, 0.45, 0.45);
        this.ifc = new IfcVisualSettings(this._lighting, viewer);
    }
    /**
     * Current global lighting settings. These settings control
     * how the scene is lit and affects all objects (both IFC and standard BabylonJS objects).
     */
    get lighting() {
        return this._lighting;
    }
}
//# sourceMappingURL=VisualSettings.js.map