#include "/lib/fog/fog_common.glsl"
#include "/lib/fog/fogloop.glsl"

float GetBorderFogMixFactor(in vec3 eyePlayerPos, in float far, in float z0)
{

    float eyeDist = length(eyePlayerPos);
    float borderFogFactor = smoothstep(far * 0.5, far, eyeDist);
    borderFogFactor *= borderFogFactor * borderFogFactor;
    return (eyeDist > far * 0.5) ? borderFogFactor : 0.0;
}
// Constants

// Inlined cloud resolver

float ComputeGodRays(vec2 fragCoord, vec3 viewDir)
{
    // Constants for god ray parameters
    const float exposure = 0.001;       // Exposure multiplier (baked weight)
    const float density = 0.75;         // Ray density (controls spread)
    const int NUM_SAMPLES = 2;          // Number of samples (unrolled loop)
    const float skyEdge = 0.9985;       // Depth threshold for "sky" detection

    // Step 1: Initial dither for sample offset
    float initialDither = blueNoise(gl_FragCoord.xy);

    // Step 2: Convert sun direction to screen space
    vec2 lightScreenPos = WorldToScreenPos(sunVec);

    // Step 3: Calculate ray direction per sample
    vec2 ray = (lightScreenPos - fragCoord) * (density / float(NUM_SAMPLES));

    // Step 4: Early out if the sun is off-screen (optional)
    // if (all(lessThan(lightScreenPos, vec2(0.0))) || all(greaterThan(lightScreenPos, vec2(1.0))))
    //     return 0.0;

    // Step 5: Initialize accumulation variables
    float decayFactor = 1.0;
    float godRayIntensity = 0.0;
    vec2 uv = fragCoord + ray * initialDither;

    // Step 6: Unrolled loop for sample accumulation
    for (int i = 0; i < NUM_SAMPLES; ++i)
    {
        // Check if current UV is within screen bounds
        float inBounds = step(0.0, uv.x) * step(0.0, uv.y) * step(uv.x, 1.0) * step(uv.y, 1.0);

        // Fetch depth and compute sky factor
        float depth = texture(depthtex0, uv).r;
        float sky = smoothstep(skyEdge, 1.0, depth);

        // Accumulate contribution
        godRayIntensity += decayFactor * sky * inBounds;

        // Update decay factor based on sky and bounds
        decayFactor *= mix(0.1, 1.0, sky * inBounds);

        // Early exit if decay is negligible
        if (decayFactor < 0.001)
            decayFactor = 0.0;

        // Move to next sample
        uv += ray;
    }

    // Step 7: Check if current pixel is a hand (depth < 0.6)
    float depthAtFrag = texture(depthtex0, fragCoord).r;
    bool isHand = depthAtFrag < 0.6;

    if (isHand)
        godRayIntensity = 0.0;

    // Step 8: Compute angle fade based on view direction and light direction
    float angleFade = pow(smoothstep(-1.0, 1.0, dot(normalize(viewDir), lightPos)), 4.0);

    // Step 9: Micro-dither for anti-aliasing
    float microDither = blueNoise(gl_FragCoord.xy + vec2(123.456, 789.123));

    // Step 10: Final result with exposure, angle fade, and dither
    return clamp(godRayIntensity * exposure * angleFade + microDither * 1e-6, 0.0, 1.0);
}
vec3 getVolumetricRays3(
    vec3 solid,
    vec4 translucent,
    inout vec3 sky,
    vec3 fragpos,
    vec3 fragpos0,
    bool iswater)
{
    // ============================================================================
    // 1. Configuration & Constants
    // ============================================================================
    const float MAX_Z_SCALE = 32.0;
    const float WATER_DENSITY_SCALE = 8.0;
    const float DEPTH_SHADOW_SCALE = 8.0;

    // ============================================================================
    // 2. Precompute Eye Parameters
    // ============================================================================
    float eyeBright = 1.0;
#ifdef OVERWORLD
    eyeBright = clamp(eyeBrightnessSmooth.y / 240.0, 0.0, 1.0);
#endif

    float estEyeDepth = clamp((14.0 - eyeBrightnessSmooth.y / 255.0 * 16.0) / 14.0, 0.0, 1.0);
    estEyeDepth = estEyeDepth * estEyeDepth * estEyeDepth * DEPTH_SHADOW_SCALE;

    // ============================================================================
    // 3. World Space Conversion
    // ============================================================================
    vec3 p31 = toWorldSpace(fragpos);
    vec3 p30 = toWorldSpace(fragpos0);

    // ============================================================================
    // 4. Distance & Direction Calculations
    // ============================================================================
    float lenPos = length(fragpos);
    float lenPos0 = length(fragpos0);
    float Vdiff = distance(fragpos, fragpos0);

    // ============================================================================
    // 5. Water/Underwater Detection
    // ============================================================================
    bool eyeInWater = (isEyeInWater == 1);

    // ============================================================================
    // 6. Volumetric Lighting Setup
    // ============================================================================
    vec3 absorbance_w = vec3(1.0);
    vec3 vL_w = vec3(0.0);
    float density = 0.0, density2 = 0.0;

    // ============================================================================
    // 7. Shadow Space Calculations
    // ============================================================================
    vec3 start = shadowSpace0();
    vec3 end = toShadowSpaceProjected(fragpos);
    vec3 dV = end - start;
    vec3 dVWorld = p31 - gbufferModelViewInverse[3].xyz;

    // ============================================================================
    // 8. Environment Parameters
    // ============================================================================
    vec3 ambient = atmosphereUp;
    vec3 sunColor = sunLight;

    float fogAmount = 0.0;
#ifdef NETHER
    fogAmount = 1.0;
#elif defined(END)
    fogAmount = 1.0;
#elif defined(OVERWORLD)
    fogAmount = getFogDensity();
#endif

    // ============================================================================
    // 9. Ray Length Adjustments
    // ============================================================================
    float rayLength = lenPos;
    float rayLength0 = lenPos0;

#ifdef OVERWORLD
    float maxZ = min(rayLength, MAX_Z_SCALE) / (rayLength + 1e-8);

    if (eyeInWater)
    {
        rayLength = lenPos * maxZ;
        dV = dV * maxZ;
        rayLength0 = lenPos0 * maxZ;
    }
    else
    {
        float gbfFrag = gbf(fragpos, far, 0);
        float gbfFrag0 = gbf(fragpos0, far, 0);
        rayLength = gbfFrag * 1028.0;
        rayLength0 = gbfFrag0 * 1028.0;

        if (iswater)
        {
            rayLength = mix(Vdiff, 14.0, gbfFrag0);
            rayLength0 = rayLength;
        }
    }
#endif

    float rayLength2 = iswater ? rayLength : rayLength0;

    // ============================================================================
    // 10. Directional Data Preparation
    // ============================================================================
    float sh0 = 1.0;
    float depthshadow = 0.0;

    // ============================================================================
    // 11. Volumetric Lighting Computation
    // ============================================================================
    vec3 worldPosAbsorb = iswater ? p31 : p30;

#ifdef VOLUMETRIC_LIGHTING
    float sunCaustics = 0.5;
    float dY = (dVWorld.y / max(length(dVWorld), 1e-6)) * rayLength;
    bool waterbool = iswater || eyeInWater;

    shadowing(depthshadow, sh0, density, sunCaustics, waterbool, start, dV, p31, fogAmount);
    waterNomal(sunCaustics, rayLength2, dY, estEyeDepth, refract(lightPos, vec3(0.0, -1.0, 0.0), 0.9), ambient, sunColor, sh0, absorbance_w, vL_w, p31, worldPosAbsorb);
#else
    waterBasic(rayLength2, dVWorld, estEyeDepth, ambient, sunColor, absorbance_w, vL_w, p31, worldPosAbsorb);
#endif

#ifdef OVERWORLD
    #ifdef VOLUMETRIC_LIGHTING_FAKE
    density += ComputeGodRays(texcoord, p31) * fogAmount * eyeBright;
    #endif
#endif

    // ============================================================================
    // 12. Density & Fog Adjustments
    // ============================================================================
    density2 = mix(fogAmount, WATER_DENSITY_SCALE, rainStrength);

#ifdef NETHER
    density2 = 16.0;
#elif defined(END)
    density2 = 8.0;
#endif

    // ============================================================================
    // 13. Atmospheric Effects
    // ============================================================================
    float rayLengthGround0 = (pow(lenPos0, FOG_FALLOFF) / 4096.0) * 200000.0 * density2;
    float rayLengthGround1 = (pow(lenPos, FOG_FALLOFF) / 4096.0) * 200000.0 * density2;

#ifndef OVERWORLD
    rayLengthGround0 = mix(rayLengthGround0, 200000.0, smoothstep(far * 0.6, far, lenPos0));
    rayLengthGround1 = mix(rayLengthGround1, 200000.0, smoothstep(far * 0.6, far, lenPos));
#endif

    vec3 transmittance;
    vec3 vL_a0 = fogAtmosphereALT2(p30, rayLengthGround0, rayLengthGround0, transmittance, density, sh0, depthshadow);
    vec3 vL_a1 = fogAtmosphereALT2(p31, rayLengthGround1, rayLengthGround1, transmittance, density, sh0, depthshadow);

// ============================================================================
// 14. Sky Blending (OVERWORLD Only)
// ============================================================================
#ifdef OVERWORLD
    if (isEyeInWater <= 1)
    {
        applySkyBlend2(transmittance, vec3(0.0), p31, far);
        applySkyBlend2(vL_a0, sky, p30, far);
        applySkyBlend2(vL_a1, sky, p31, far);
    }
#endif

    // ============================================================================
    // 15. Final Volumetric Light Mixing
    // ============================================================================
    vec3 vL_a = (!iswater || eyeInWater) ? mix(vL_a0, vL_a1, translucent.a) : vL_a1;
    vL_a *= eyeBright;

    if (!eyeInWater)
        vL_w *= eyeBright;

// ============================================================================
// 16. Final Effects Application
// ============================================================================
#ifndef OVERWORLD
    if (isEyeInWater <= 1)
        applySkyBlend(solid, translucent, sky, vL_w, p31, p30, far, iswater);
#endif

    applyWaterEffects(
        solid,
        translucent,
        sky,
        vL_w,
        vL_a,
        absorbance_w,
        transmittance,
        fogColor,
        fragpos,
        p30,
        isEyeInWater,
        iswater);

    // ============================================================================
    // 17. Final Color Composition
    // ============================================================================
    return blendTranslucencies(solid, translucent);
}