/***********************************************/
/*           Copyright (C) 2024 Belmu          */
/*             All Rights Reserved             */
/***********************************************/

#include "/settings.glsl"

layout (local_size_x = 8, local_size_y = 4, local_size_z = 1) in;

const vec2 workGroupsRender = vec2(renderScale, renderScale);

layout (rgba32f) uniform image2D colorimg0;

#include "/include/common.glsl"
#include "/include/utility/transforms.glsl"

#include "/include/voxels/voxelization.glsl"
#include "/include/materials/material.glsl"
#include "/include/voxels/raytracer.glsl"

#if DEBUG_ALBEDO == 0 && DEBUG_NORMALS == 0 && DEBUG_HIT_POSITION == 0

    #include "/include/atmospherics/constants.glsl"
    #include "/include/atmospherics/celestial_bodies.glsl"

    #include "/include/utility/complex.glsl"

    #include "/include/fragment/bsdf/fresnel.glsl"

    #include "/include/camera/lens/common.glsl"

    #if LENS_TYPE == 0
        #include "/include/camera/lens/thick_lens.glsl"
    #elif LENS_TYPE == 1
        #include "/include/camera/lens/thin_lens.glsl"
    #endif

    #include "/include/atmospherics/atmosphere.glsl"
    #include "/include/atmospherics/celestial_paths.glsl"

    #include "/include/fragment/bsdf/evaluate.glsl"

#endif

void main() {
    vec2 fragCoords = vec2(gl_GlobalInvocationID.xy / renderScale);
    if (fragCoords.x > viewWidth || fragCoords.y > viewHeight) return;

    fragCoords /= viewSize;

    vec3 screenPosition = vec3(fragCoords, 0.0);
    vec3 viewPosition   = screenToView(screenPosition);
    vec3 sceneDirection = normalize(mat3(gbufferModelViewInverse) * viewPosition);

    vec3 radianceOut = vec3(0.0);

    #if DEBUG_ALBEDO == 1 || DEBUG_NORMALS == 1 || DEBUG_HIT_POSITION == 1
    
        /* Debug */
        VoxelIntersection hit = raytraceVoxel(cameraPosition, sceneDirection, MAX_RAYTRACE_STEPS);

        if (hit.intersect) {
            #if DEBUG_ALBEDO == 1
                radianceOut = getVoxelAlbedoRGB(hit.voxel.packedData, hit.textureCoords);

            #elif DEBUG_NORMALS == 1
                radianceOut = getVoxelMaterial(hit.voxel.packedData, hit.textureCoords, hit.position, hit.normal, 0.0).normal * 0.5 + 0.5;

            #elif DEBUG_HIT_POSITION == 1
                radianceOut = (hit.position - ivec3(floor(hit.position)) - vec3(0.5)) * 0.5 + 0.5;
            #endif
        }

    #else

        if (hideGUI == 0)
            return;

        bool canRender = true;
        #if CHECKERBOARD_RENDERING == 1
            ivec2 coords    = ivec2(gl_GlobalInvocationID.xy / int(viewWidth * 0.1));
                  canRender = (coords.x + coords.y & 1) == (frameCounter & 1);
        #endif

        vec3 cmfWeights = estimateCMFWeights(RADIANCE_HISTORY_BUFFER, fragCoords);

        for (int pathCount = 0; pathCount < MAX_PT_SAMPLES; pathCount++) {
            /* CIE Importance Sampling */
            float wavelength = binarySearchCIE(0.0, 1.0, randF(), cmfWeights) * 441.0 + 390.0;

            airIOR = airDispersion(wavelength, 15.0, 101325.0, 0.0, 400.0);

            /* Path Setup */
            float throughput = 1.0;

            vec3 sensorPosition = cameraPosition;

            #if LENS_TYPE == 0
                generateThickLensCameraRay(fragCoords, wavelength, throughput, sensorPosition, sceneDirection);
            #elif LENS_TYPE == 1
                generateThinLensCameraRay(fragCoords, viewPosition, sensorPosition, sceneDirection);
            #endif

            /* BSDF Evaluation */
            if (canRender) {
                radianceOut += evaluateHeitzBSDF(throughput, sensorPosition, sceneDirection, wavelength, fragCoords, cmfWeights) / MAX_PT_SAMPLES;
            }
        }

    #endif

    imageStore(colorimg0, ivec2(gl_GlobalInvocationID.xy), vec4(radianceOut, 0.0));
}
