#include "/include/uniforms.glsl"
#include "/include/config.glsl"
#include "/include/constants.glsl"
#include "/include/common.glsl"
#include "/include/pbr.glsl"
#include "/include/main.glsl"
#include "/include/textureSampling.glsl"
#include "/include/brdf.glsl"
#include "/include/spaceConversion.glsl"
#include "/include/shadowMapping.glsl"
#include "/include/atmosphere.glsl"
#include "/include/subsurfaceScattering.glsl"
#include "/include/atmosphere.glsl"

/* RENDERTARGETS: 7 */
layout (location = 0) out vec4 color;

float fogDensityAtPoint (vec3 playerPos)
{
    return VL_DENSITY * pow(texture(texWorley, fract(rcp(128.0) * (cameraMod256 + playerPos))).r, 4.0) * exp(-0.15 * max0(eyeAltitude + playerPos.y - 64.0));
}

void main ()
{   
    ivec2 texel = ivec2(gl_FragCoord.xy);
    float depth0 = texelFetch(depthtex0, texel, 0).r;
    float depth1 = texelFetch(depthtex1, texel, 0).r;
    
    color = texelFetch(colortex7, texel, 0);

    DeferredMaterial mat = unpackMaterialData(texel);

    vec2 uv = internalTexelSize * gl_FragCoord.xy;
    vec3 playerPos = screenToPlayerPos(vec3(uv, depth0)).xyz;
    vec3 viewOrigin = screenToPlayerPos(vec3(uv, 0.0)).xyz;
    vec3 viewDir = playerPos - viewOrigin;

    vec2 dither = blueNoise(gl_FragCoord.xy).xy;

    float sqrViewLength = dot(viewDir, viewDir);
    float invViewLength = inversesqrt(sqrViewLength);

    float mu = invViewLength * dot(viewDir, shadowDir);

    if (mat.sssAmount > 0.0 && depth0 < 1.0 && depth1 == depth0) color.rgb += mat.sssAmount * subsurfaceScattering(playerPos, mat.albedo.rgb, mu, dither);
    
    #ifdef VL_ENABLED
        float stepSize = rcp(VL_STEP_COUNT) * min(VL_DISTANCE, sqrViewLength * invViewLength);

        vec3 rayStep = viewDir * invViewLength * stepSize;
        vec3 rayPos  = viewOrigin + rayStep * dither.x;

        vec3 shadowRayPos  = playerToShadowViewPos(vec4(rayPos, 1.0));

        mat3 shadowViewMatrix = tbnNormal(shadowDir);

        vec3 shadowRayStep = rayStep * shadowViewMatrix;
        vec3 shadowViewNormal = depth1 == 1.0 ? vec3(0.0) : (mat.geoNormal * shadowViewMatrix);

        float viewOpticalDepth = 0.0;

        vec3 sunlightFactor = shadowLightBrightness * lightTransmittance(shadowDir) * mix(VL_INTENSITY, VL_INTENSITY_DAY, smoothstep(0.1, 0.3, sunDir.y)) * phaseMie(mu, 0.5);
        vec3 skylight = 2.0 * smoothstep(60.0, 240.0, float(eyeBrightnessSmooth.y)) * texelFetch(texSkyView, ivec2(16, 60), 0).rgb;

        vec3 integratedData = vec3(0.0);

        for (int i = 0; i < VL_STEP_COUNT; i++, rayPos += rayStep, shadowRayPos += shadowRayStep) {
            float sampleDensity = stepSize * fogDensityAtPoint(rayPos);

            viewOpticalDepth += sampleDensity;

            if (i == 0) viewOpticalDepth *= dither.x;

            vec3 sunlight = sunlightFactor * step(textureShadow((shadowRayPos + shadowViewNormal * max0(0.1 - dot(mat.geoNormal, rayPos - playerPos))).xy), shadowRayPos.z);

            integratedData += exp(-viewOpticalDepth) * sampleDensity * (sunlight + skylight);
        }

        color.rgb += integratedData;
    #endif

    // === Volumetric Clouds v1.6.8 ===
    #if defined VOLUMETRIC_CLOUDS && defined DIMENSION_OVERWORLD
    {
        vec3 viewDir_n = normalize(viewDir);

        // Cloud layer boundaries
        float cloudBase = CLOUD_ALTITUDE - eyeAltitude;
        float cloudTop  = cloudBase + CLOUD_THICKNESS;

        // Only render clouds if looking towards cloud layer
        if ((viewDir_n.y > 0.001 && cloudBase > 0.0) || (viewDir_n.y < -0.001 && cloudTop < 0.0))
        {
            // Calculate ray entry/exit distances through cloud layer
            float tEntry = (viewDir_n.y > 0.0) ? (cloudBase / viewDir_n.y) : (cloudTop / viewDir_n.y);
            float tExit  = (viewDir_n.y > 0.0) ? (cloudTop / viewDir_n.y) : (cloudBase / viewDir_n.y);

            tEntry = max(0.0, tEntry);
            tExit  = min(tExit, depth0 < 1.0 ? length(viewDir) : 8000.0);

            if (tEntry < tExit)
            {
                float cloudStepSize = (tExit - tEntry) / float(CLOUD_SAMPLES);
                vec3 cloudRayPos = viewOrigin + viewDir_n * (tEntry + cloudStepSize * dither.x);

                float cloudTransmittance = 1.0;
                vec3 cloudRadiance = vec3(0.0);

                // Sun & sky lighting
                vec3 sunColor = shadowLightBrightness * lightTransmittance(shadowDir) * 3.0;
                vec3 ambientColor = 1.5 * texelFetch(texSkyView, ivec2(16, 60), 0).rgb;

                for (int ci = 0; ci < CLOUD_SAMPLES && cloudTransmittance > 0.01; ci++)
                {
                    vec3 worldPos = cameraMod256 + cloudRayPos;

                    // 3D noise for cloud density — wind animation
                    vec3 noiseCoord = worldPos * rcp(512.0);
                    noiseCoord.xz += frameTimeCounter * CLOUD_SPEED * 0.0003;

                    float noise1 = texture(texWorley, fract(noiseCoord)).r;
                    float noise2 = texture(texWorley, fract(noiseCoord * 2.7 + 0.3)).r;
                    float noise3 = texture(texWorley, fract(noiseCoord * 5.1 + 0.7)).r;

                    // FBM-like cloud shape
                    float cloudNoise = noise1 * 0.5 + noise2 * 0.3 + noise3 * 0.2;

                    // Height-based falloff within cloud layer
                    float relativeHeight = (cloudRayPos.y + eyeAltitude - (CLOUD_ALTITUDE - CLOUD_THICKNESS * 0.5)) / CLOUD_THICKNESS;
                    float heightFalloff = smoothstep(0.0, 0.25, relativeHeight) * smoothstep(1.0, 0.75, relativeHeight);

                    // Cloud density with coverage threshold
                    float density = cloudStepSize * CLOUD_DENSITY * heightFalloff * max(0.0, cloudNoise - (1.0 - CLOUD_COVERAGE));

                    if (density > 0.0001)
                    {
                        // Beer-Lambert extinction
                        float extinction = exp(-density * 2.0);

                        // Phase function for sun scattering (silver lining)
                        float mu = dot(viewDir_n, shadowDir);
                        float phase = 0.3 * rcp(4.0 * PI) + 0.7 * phaseMie(mu, 0.75);

                        // Light at this cloud sample
                        vec3 cloudLight = sunColor * phase + ambientColor;

                        // Integrate radiance
                        cloudRadiance += cloudTransmittance * (1.0 - extinction) * cloudLight;
                        cloudTransmittance *= extinction;
                    }

                    cloudRayPos += viewDir_n * cloudStepSize;
                }

                // Reduce clouds during rain
                float cloudRainFade = 1.0 - 0.6 * rainStrength;

                // Blend clouds with scene
                color.rgb = color.rgb * mix(1.0, cloudTransmittance, cloudRainFade) 
                          + cloudRadiance * cloudRainFade;
            }
        }
    }
    #endif
}