// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

import QtQuick
import QtQuick3D
import QtQuick3D.Helpers.impl

MainSceneEffect {
    id: sceneEffect
    property int tonemapMode: SceneEnvironment.TonemapModeLinear
    property real exposure: 1.0
    property real white: 1.0
    property bool applyFXAA: false
    property bool ditheringEnabled: false
    property real sharpnessAmount: 0.0 // 0.0 - 1.0
    property bool colorAdjustmentsEnabled: false
    property vector3d bcsAdjustments: Qt.vector3d(1.0, 1.0, 1.0)

    // Lens Flare
    property bool lensFlareEnabled: false
    property real lensFlareBloomScale: 10           // 0 - 20
    property real lensFlareBloomBias: 0.95          // 0 - x (basically maximum color value)
    property real lensFlareGhostDispersal: 0.5      // 0 - 1
    property int lensFlareGhostCount: 4             // 0 - 20
    property real lensFlareHaloWidth: 0.25          // 0 - 1
    property real lensFlareStretchToAspect: 0.5     // 0 - 1
    property real lensFlareDistortion: 5            // 0.0 - 20.0
    property real lensFlareBlurAmount: 3            // 0.0 - 5.0
    property bool lensFlareApplyDirtTexture: false
    property bool lensFlareApplyStarburstTexture: false
    property vector3d lensFlareCameraDirection: Qt.vector3d(0, 0, -1)
    property bool lensFlareDebug: false

    property TextureInput lensColorTexture: TextureInput {
        id: lensColorTextureInput
        texture: defaultLensColorTexture
    }
    property alias lensColorTextureAlias: lensColorTextureInput.texture
    Texture {
        id: defaultLensColorTexture
        source: "qrc:/qtquick3d_helpers/images/gradientTexture.png"
        tilingModeHorizontal: Texture.ClampToEdge
        tilingModeVertical: Texture.ClampToEdge
    }

    property TextureInput lensDirtTexture: TextureInput {
        id: lensDirtTextureInput
        texture: defaultLensColorTexture
    }
    property alias lensDirtTextureAlias: lensDirtTextureInput.texture
    Texture {
        id: defaultLensDirtTexture
        source: "qrc:/qtquick3d_helpers/images/lens_dirt_default.jpeg"
    }

    property TextureInput starburstTexture: TextureInput {
        id: lensStarburstTextureInput
        texture: defaultLensStarburstTexture
    }
    property alias starburstTextureAlias: lensStarburstTextureInput.texture
    Texture {
        id: defaultLensStarburstTexture
        source: "qrc:/qtquick3d_helpers/images/noiseTexture.png"
    }

    // Glow data
    readonly property bool isFirstPass: true
    property bool isGlowEnabled: false
    property bool glowQualityHigh: false
    property bool glowUseBicubicUpscale: false
    property real glowStrength : 1.0        // 0.0 - 2.0
    property real glowIntensity : 0.8       // 0.0 - 8.0
    property real glowBloom : 0.0           // 0.0 - 1.0
    property int glowBlendMode : 2          // Additive,Screen,Softlight,Replace
    property real glowHDRMaximumValue: 12.0 // 0.0 - 256.0
    property real glowHDRScale: 2.0         // 0.0 - 4.0
    property real glowHDRMinimumValue: 1.0     // 0.0 - 4.0
    property int glowLevel: 1               // 1 - 7

    // Color Grading (LUT)
    property bool enableLut: false
    property alias lutTextureAlias: lutTextureInput.texture
    property TextureInput lut: TextureInput {
        id: lutTextureInput
        texture: defaultLutTexture
    }
    property real lutSize: 16.0 // size of texture, textures are 3d in 2d, so width = lutSize * lutSize, height = lutSize
    property real lutFilterAlpha: 1.0 // 0.0 - 1.0
    Texture {
        id: defaultLutTexture
        source: "qrc:/qtquick3d_helpers/luts/identity.png"
    }

    // Vignette
    property bool vignetteEnabled: false
    property real vignetteStrength: 15 // 0 - 15
    property color vignetteColor: "gray"
    property real vignetteRadius: 0.35 // 0 - 5

    readonly property TextureInput glowBuffer1: TextureInput {
        texture: Texture {}
    }
    readonly property TextureInput glowBuffer2: TextureInput {
        texture: Texture {}
    }
    readonly property TextureInput glowBuffer3: TextureInput {
        texture: Texture {}
    }
    readonly property TextureInput glowBuffer4: TextureInput {
        texture: Texture {}
    }
    readonly property TextureInput glowBuffer5: TextureInput {
        texture: Texture {}
    }
    readonly property TextureInput glowBuffer6: TextureInput {
        texture: Texture {}
    }
    readonly property TextureInput glowBuffer7: TextureInput {
        texture: Texture {}
    }

    readonly property TextureInput lensFlareDownsampleBuffer: TextureInput {
        texture: Texture {}
    }

    readonly property TextureInput lensFlareFeaturesBuffer: TextureInput {
        texture: Texture {}
    }

    readonly property TextureInput lensFlareTexture: TextureInput {
        texture: Texture {}
    }

    Component.onCompleted: buildPasses()

    onIsGlowEnabledChanged: buildPasses()
    onLensFlareEnabledChanged: buildPasses()

    function buildPasses() {
        let passList = [];
        if (lensFlareEnabled) {
            passList.push(lensFlareDownsamplePass)
            passList.push(lensFlareFeaturesPass)
            passList.push(lensFlareBlurHorizontalPass)
            passList.push(lensFlareBlurVerticalPass)
        }

        if (isGlowEnabled) {
            passList.push(horizontalBlurPass1)
            passList.push(verticalBlurPass1)
            passList.push(horizontalBlurPass2)
            passList.push(verticalBlurPass2)
            passList.push(horizontalBlurPass3)
            passList.push(verticalBlurPass3)
            passList.push(horizontalBlurPass4)
            passList.push(verticalBlurPass4)
            passList.push(horizontalBlurPass5)
            passList.push(verticalBlurPass5)
            passList.push(horizontalBlurPass6)
            passList.push(verticalBlurPass6)
            passList.push(horizontalBlurPass7)
            passList.push(verticalBlurPass7)
        }

        passList.push(tonemapPass)
        tonemapPass.rebuildCommands();

        sceneEffect.passes = passList // qmllint disable read-only-property
    }

    Shader {
        id: tonemapperFrag
        stage: Shader.Fragment
        shader: "qrc:/qtquick3d_helpers/shaders/tonemapper.frag"
    }

    Shader {
        id: glowHorizontalBlur
        stage: Shader.Fragment
        shader: "qrc:/qtquick3d_helpers/shaders/glowhorizontalblur.frag"
    }

    Shader {
        id: glowVerticalBlur
        stage: Shader.Fragment
        shader: "qrc:/qtquick3d_helpers/shaders/glowverticalblur.frag"
    }

    Shader {
        id: lensFlareDownsample
        stage: Shader.Fragment
        shader: "qrc:/qtquick3d_helpers/shaders/lensflaredownsample.frag"
    }

    Shader {
        id: lensFlareFeatures
        stage: Shader.Fragment
        shader: "qrc:/qtquick3d_helpers/shaders/lensflarefeatures.frag"
    }

    Shader {
        id: lensFlareVerticalBlurVert
        stage: Shader.Vertex
        shader: "qrc:/qtquick3d_helpers/shaders/lensflareblurvertical.vert"
    }
    Shader {
        id: lensFlareHorizontalVert
        stage: Shader.Vertex
        shader: "qrc:/qtquick3d_helpers/shaders/lensflareblurhorizontal.vert"
    }
    Shader {
        id: lensFlareGaussianBlur
        stage: Shader.Fragment
        shader: "qrc:/qtquick3d_helpers/shaders/lensflaregaussianblur.frag"
    }

    Buffer {
        id: tempBuffer1
        name: "tempBuffer1"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.5
    }
    Buffer {
        id: tempBuffer2
        name: "tempBuffer2"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.25
    }
    Buffer {
        id: tempBuffer3
        name: "tempBuffer3"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.125
    }
    Buffer {
        id: tempBuffer4
        name: "tempBuffer4"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.0625
    }
    Buffer {
        id: tempBuffer5
        name: "tempBuffer5"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.03125
    }
    Buffer {
        id: tempBuffer6
        name: "tempBuffer6"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.015625
    }
    Buffer {
        id: tempBuffer7
        name: "tempBuffer7"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.0078125
    }

    Buffer {
        id: glowBuffer1
        name: "glowBuffer1"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.5
    }
    Buffer {
        id: glowBuffer2
        name: "glowBuffer2"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.25
    }
    Buffer {
        id: glowBuffer3
        name: "glowBuffer3"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.125
    }
    Buffer {
        id: glowBuffer4
        name: "glowBuffer4"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.0625
    }
    Buffer {
        id: glowBuffer5
        name: "glowBuffer5"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.03125
    }
    Buffer {
        id: glowBuffer6
        name: "glowBuffer6"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.015625
    }
    Buffer {
        id: glowBuffer7
        name: "glowBuffer7"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.0078125
    }

    Buffer {
        id: lensFlareDownsampleBuffer
        name: "lensFlareDownsampleBuffer"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.5
    }

    Buffer {
        id: lensFlareFeaturesBuffer
        name: "lensFlareFeaturesBuffer"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.5
    }

    Buffer {
        id: lensFlareBlurTempBuffer
        name: "lensFlareBlurTempBuffer"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.5
    }

    Buffer {
        id: lensFlareBlurBuffer
        name: "lensFlareBlurBuffer"
        format: Buffer.RGBA16F
        textureFilterOperation: Buffer.Linear
        textureCoordOperation: Buffer.ClampToEdge
        bufferFlags: Buffer.None
        sizeMultiplier: 0.5
    }

    Pass {
        id: horizontalBlurPass1
        shaders: [glowHorizontalBlur]
        commands: [
            SetUniformValue {
                target: "isFirstPass"
                value: true
            }
        ]
        output: tempBuffer1
    }

    Pass {
        id: verticalBlurPass1
        shaders: [glowVerticalBlur]
        commands: [
            SetUniformValue {
                target: "isFirstPass"
                value: false
            },
            BufferInput {
                buffer: tempBuffer1
            }
        ]
       output: glowBuffer1
    }

    Pass {
        id: horizontalBlurPass2
        shaders: [glowHorizontalBlur]
        commands: [
            SetUniformValue {
                target: "isFirstPass"
                value: false
            },
            BufferInput {
                buffer: glowBuffer1
            }
        ]
        output: tempBuffer2
    }

    Pass {
        id: verticalBlurPass2
        shaders: [glowVerticalBlur]
        commands: [
            SetUniformValue {
                target: "isFirstPass"
                value: false
            },
            BufferInput {
                buffer: tempBuffer2
            }
        ]
        output: glowBuffer2
    }

    Pass {
        id: horizontalBlurPass3
        shaders: [glowHorizontalBlur]
        commands: [
            SetUniformValue {
                target: "isFirstPass"
                value: false
            },
            BufferInput {
                buffer: glowBuffer2
            }
        ]
        output: tempBuffer3
    }

    Pass {
        id: verticalBlurPass3
        shaders: [glowVerticalBlur]
        commands: [
            SetUniformValue {
                target: "isFirstPass"
                value: false
            },
            BufferInput {
                buffer: tempBuffer3
            }
        ]
        output: glowBuffer3
    }

    Pass {
        id: horizontalBlurPass4
        shaders: [glowHorizontalBlur]
        commands: [
            SetUniformValue {
                target: "isFirstPass"
                value: false
            },
            BufferInput {
                buffer: glowBuffer3
            }
        ]
        output: tempBuffer4
    }

    Pass {
        id: verticalBlurPass4
        shaders: [glowVerticalBlur]
        commands: [
            SetUniformValue {
                target: "isFirstPass"
                value: false
            },
            BufferInput {
                buffer: tempBuffer4
            }
        ]
        output: glowBuffer4
    }

    Pass {
        id: horizontalBlurPass5
        shaders: [glowHorizontalBlur]
        commands: [
            SetUniformValue {
                target: "isFirstPass"
                value: false
            },
            BufferInput {
                buffer: glowBuffer4
            }
        ]
        output: tempBuffer5
    }

    Pass {
        id: verticalBlurPass5
        shaders: [glowVerticalBlur]
        commands: [
            SetUniformValue {
                target: "isFirstPass"
                value: false
            },
            BufferInput {
                buffer: tempBuffer5
            }
        ]
        output: glowBuffer5
    }

    Pass {
        id: horizontalBlurPass6
        shaders: [glowHorizontalBlur]
        commands: [
            SetUniformValue {
                target: "isFirstPass"
                value: false
            },
            BufferInput {
                buffer: glowBuffer5
            }
        ]
        output: tempBuffer6
    }

    Pass {
        id: verticalBlurPass6
        shaders: [glowVerticalBlur]
        commands: [
            SetUniformValue {
                target: "isFirstPass"
                value: false
            },
            BufferInput {
                buffer: tempBuffer6
            }
        ]
        output: glowBuffer6
    }
    Pass {
        id: horizontalBlurPass7
        shaders: [glowHorizontalBlur]
        commands: [
            SetUniformValue {
                target: "isFirstPass"
                value: false
            },
            BufferInput {
                buffer: glowBuffer6
            }
        ]
        output: tempBuffer7
    }

    Pass {
        id: verticalBlurPass7
        shaders: [glowVerticalBlur]
        commands: [
            SetUniformValue {
                target: "isFirstPass"
                value: false
            },
            BufferInput {
                buffer: tempBuffer7
            }
        ]
        output: glowBuffer7
    }

    Pass {
        id: lensFlareDownsamplePass
        shaders: [lensFlareDownsample]
        output: lensFlareDownsampleBuffer
    }

    Pass {
        id: lensFlareFeaturesPass
        shaders: [lensFlareFeatures]
        commands: [
            BufferInput {
                buffer: lensFlareDownsampleBuffer
                sampler: "lensFlareDownsampleBuffer"
            }
        ]
        output: lensFlareFeaturesBuffer
    }

    Pass {
        id: lensFlareBlurHorizontalPass
        shaders: [lensFlareHorizontalVert, lensFlareGaussianBlur]
        commands: [
            BufferInput {
                buffer: lensFlareFeaturesBuffer
                sampler: "lensFlareTexture"
            }
        ]
        output: lensFlareBlurTempBuffer
    }
    Pass {
        id: lensFlareBlurVerticalPass
        shaders: [lensFlareVerticalBlurVert, lensFlareGaussianBlur]
        commands: [
            BufferInput {
                buffer: lensFlareBlurTempBuffer
                sampler: "lensFlareTexture"
            }

        ]
        output: lensFlareBlurBuffer
    }

    Connections {
        target: sceneEffect
        function onIsGlowEnabledChanged() { tonemapPass.rebuildCommands() }
        function onLensFlareEnabledChanged() { tonemapPass.rebuildCommands() }
    }

    BufferInput {
        id: glowBufferInput1
        buffer: glowBuffer1
        sampler: "glowBuffer1"
    }
    BufferInput {
        id: glowBufferInput2
        buffer: glowBuffer2
        sampler: "glowBuffer2"
    }
    BufferInput {
        id: glowBufferInput3
        buffer: glowBuffer3
        sampler: "glowBuffer3"
    }
    BufferInput {
        id: glowBufferInput4
        buffer: glowBuffer4
        sampler: "glowBuffer4"
    }
    BufferInput {
        id: glowBufferInput5
        buffer: glowBuffer5
        sampler: "glowBuffer5"
    }
    BufferInput {
        id: glowBufferInput6
        buffer: glowBuffer6
        sampler: "glowBuffer6"
    }
    BufferInput {
        id: glowBufferInput7
        buffer: glowBuffer7
        sampler: "glowBuffer7"
    }
    BufferInput {
        id: lensFlareBufferInput
        buffer: lensFlareBlurBuffer
        sampler: "lensFlareTexture"
    }

    Pass {
        id: tonemapPass;
        shaders: [tonemapperFrag]

        function rebuildCommands() {
            let dynamicCommands = []
            if (sceneEffect.isGlowEnabled) {
                dynamicCommands.push(glowBufferInput1)
                dynamicCommands.push(glowBufferInput2)
                dynamicCommands.push(glowBufferInput3)
                dynamicCommands.push(glowBufferInput4)
                dynamicCommands.push(glowBufferInput5)
                dynamicCommands.push(glowBufferInput6)
                dynamicCommands.push(glowBufferInput7)
            }
            if (sceneEffect.lensFlareEnabled) {
                dynamicCommands.push(lensFlareBufferInput)
            }
            tonemapPass.commands = dynamicCommands; // qmllint disable read-only-property
        }
    }
}
