
#include "/lib/shadows/shadow_misc.glsl"
float gbf(in vec3 eyePlayerPos, in float far)
{
    // Calculate the horizontal (XZ) and vertical (Y) distances
    float distXZ = length(vec2(eyePlayerPos.x, eyePlayerPos.z));
    float distY = abs(eyePlayerPos.y);

    // Max distance between horizontal and vertical distances
    float eyeDist = max(distXZ, distY);

    // Compute the border fog factor
    float borderFogFactor = smoothstep(far * 0.9, far, eyeDist);
    borderFogFactor *= borderFogFactor * borderFogFactor; // Cubic easing

    return borderFogFactor;
}

// Precompute any other static values if possible
float shadowFunc(
    float diffuseSun,
    float sssAmount,
    vec3 p3,
    float noise,
    vec2 lightmap,
    vec3 fragpos,
    inout vec3 sAlbedo,
    vec3 albedo,
    vec3 normals,
    inout vec3 colored,
    float entity)
{
    // Compute absolute values of p3's x and z components
    vec2 absp3 = abs(p3.xz);
    float shading = 1.0;
    float sssBackScatter = 1.0;

    // Early exit if lightmap.y is negligible
    if (lightmap.y < 0.001)
    {
        return 0.0;
    }

    // Precompute mat3 and diagonal components from shadowModelView and shadowProjection
    mat3 shadowModelViewMat3 = mat3(shadowModelView);
    vec3 shadowProjectionDiag = vec3(
        shadowProjection[0].x,
        shadowProjection[1].y,
        shadowProjection[2].z);

    float mix1 = interpolate(shadowPosWorld.y).x;

#ifdef DISTANT_HORIZONS
    mix1 = 1.0;
#else
    mix1 *= 0.01 * (1.0 - sssAmount);
#endif

    // Compute Subsurface Scattering (SSS) factor
    float SSS = sssAmount * 0.75 * max(diffuseSun, 0.5);

    // Offset p3 slightly to mitigate shadow acne
    p3 += normalize(shadowPosWorld) * 0.05;

    // Transform position and normals using shadowModelView matrix
    vec3 shadowModelViewP3 = shadowModelViewMat3 * p3;
    vec3 shadowModelViewNormals = shadowModelViewMat3 * normals;
    vec3 shadowModelViewTranslation = shadowModelView[3].xyz;

    // Project shadow position using precomputed diagonal projection matrix
    vec3 ProjShadowPos = shadowProjectionDiag * (shadowModelViewP3 + shadowModelViewTranslation) + shadowProjection[3].xyz;

    // Calculate distortion factor based on projected shadow position
    float distortFac = calcDistort(ProjShadowPos.xy);

    // Compute mix2 based on whether DISTANT_HORIZONS is defined
    float mix2;
#ifdef DISTANT_HORIZONS
    mix2 = 0.0;
#else
    // Approximate pow(sssAmount, 0.01) with a linear function
    float pow_sssAmount_0_01_mix2 = 1.0 + 0.01 * (sssAmount - 1.0);
    mix2 = mix(distortFac * 0.01, 0.04, pow_sssAmount_0_01_mix2);
#endif

    // Apply Subsurface Scattering (SSS) adjustment to the projected shadow position
    ProjShadowPos += shadowProjectionDiag * shadowModelViewNormals * mix2;

    // Initialize depth-related variables
    float pdepth = 0.0;
    float avgDepth = 0.3;

#ifndef DISTANT_HORIZONS
    // Conditional check based on shadow distance
    if (absp3.x < shadowDistance && absp3.y < shadowDistance)
#else
    // If DISTANT_HORIZONS is defined, always execute the block
    if (true)
#endif
    {
        // Reset SSS factor
        SSS = 0.0;

        // Sample blue noise texture for jittering
        vec4 noise_2 = blueNoise4(gl_FragCoord.xy);

        // Apply distortion factor to the x and y components of ProjShadowPos
        ProjShadowPos.xy *= distortFac;

        // Scale and bias ProjShadowPos for texture sampling
        ProjShadowPos = ProjShadowPos * vec3(0.5, 0.5, 0.083333) + 0.5;

        // Reset shading and depth values
        shading = 0.0;
        pdepth = 0.0;
        avgDepth = 0.0;

        // Compute reciprocal of shadowMapResolution once to replace divisions
        float inv_shadowMapResolution = 1.0 / shadowMapResolution;

        // Compute rdMul0 using precomputed reciprocal
        float d01k = 0.2 * distortFac;
        float rdMul0 = d01k * inv_shadowMapResolution;

        // Define the number of shadow samples
        const int iterations = SHADOW_SAMPLES;
        float scaleFac = distortFac;

        // Define filter depth values, modified if DISTANT_HORIZONS is defined
        vec3 filterDepth = vec3(1.0, 16.0, 128.0);
#ifdef DISTANT_HORIZONS
        filterDepth = vec3(3.0, 16.0, 128.0);
#endif

        // Compute RadiusMax using precomputed reciprocal
        float RadiusMax = distortFac * inv_shadowMapResolution;

        // Initialize blocker count and average blocker depth
        float blockerCount = 0.0;
        float avgBlockerDepth0 = 0.0;
        float filterDepthRange = filterDepth.y - filterDepth.x;

        float threshMulMult2 = (eyeAltitude > 500.0) ? 8192.0 : (eyeAltitude > 400.0) ? 4096.0
                                                                                      : 2048.0;

        float threshMul = max(threshMulMult2 * inv_shadowMapResolution * shadowDistance * 0.01, 0.95);

        // Compute distortThresh and diffthresh
        float oneMinusDiffuseSunSq = 1.0 - diffuseSun * diffuseSun;
        float sqrtOneMinusDiffuseSunSq = sqrt(oneMinusDiffuseSunSq);
        float distortThresh = clamp(sqrtOneMinusDiffuseSunSq / diffuseSun, 0.0, mix1) / scaleFac;
        float diffthresh = (distortThresh / 6000.0) * threshMul;

        // Precompute reciprocal of iterations to replace divisions
        float inv_iterations = 1.0 / float(iterations);

        // Initialize counter for blue noise sampling
        int counter = (frameCounter % 40000) * iterations;

        // Compute prtecomp using precomputed values
        float prtecomp = RadiusMax * 2.7 / (float(iterations) * shadowMapResolution * scaleFac);

        // Initialize shadow color accumulation
        vec4 shadowcolor = vec4(0.0);

        // Iterate over shadow samples
        for (int i = 0; i < iterations; i++)
        {
            // Compute backscatter for SSS
            float shadingIter = shading * inv_iterations;
            float scatterFactor = clamp(diffuseSun * 16.0 * shadingIter + shadingIter, 0.0, 1.0);
            sssBackScatter = mix(mix(1.0, 16.0, sssAmount), 1.0, scatterFactor);

            // Generate jittered sample offsets using blue noise
            vec2 ij = fract(R2_samples3(counter + i) + noise_2.rg);
            vec2 offset = tapLocationBlueNoise(i, iterations, 80, ij);

            // Sample the first shadow texture (assumed to be a depth texture)
            float d = texture(shadowtex0, ProjShadowPos.xy + offset * RadiusMax).x;

            // Compute depth difference and shadow weight
            float zDiff = ProjShadowPos.z - d;
            avgDepth += max(zDiff, 0.0) * mix(2048, 512, sssAmount);

            float weightpu = 3.0 + (float(i) + noise) * prtecomp;
            float diffthreshHalf = 0.5 * diffthresh;
            float shadowWeight = smoothstep(weightpu * diffthreshHalf, weightpu * diffthresh, zDiff);

            // Accumulate blocker count and average blocker depth
            blockerCount += shadowWeight;
            avgBlockerDepth0 += d * shadowWeight;

            // Compute average blocker depth
            float avgBlockerDepth;
            if (blockerCount > 0.0)
                avgBlockerDepth = avgBlockerDepth0 * (1.0 / blockerCount);
            else
                avgBlockerDepth = ProjShadowPos.z;

            // Compute pdepth based on blocker depth and filter depth
            float t0 = max((ProjShadowPos.z - avgBlockerDepth) / filterDepth.z, 0.0) * 1500.0;
            pdepth = (t0 * filterDepthRange + filterDepth.x) * (blockerCount > 0.9 ? 1.0 : 0.0) * rdMul0;

            // Adjust shadow coordinates with SSS backscatter and distortion
            vec3 shadowCoordWeighted = ProjShadowPos + vec3(offset * pdepth * sssBackScatter,
                                                            -diffthresh * (1.0 + noise * shadowMapResolution * pdepth * inv_iterations));

            // Sample the second shadow texture (assumed to be a depth texture)
            float shadow0 = texture(shadowtex1, shadowCoordWeighted);

#ifdef COLORED_SHADOWS
            // Colored shadow calculations
            float shadow1 = texture(shadow, shadowCoordWeighted);
            shadowcolor += texture(shadowcolor0, shadowCoordWeighted.xy);
            float transparentShadow = abs(shadow1 - shadow0);

            // Accumulate shading based on transparentShadow
            if (transparentShadow > 0.0)
            {
                shading += clamp(shadow1 + clamp(1.0 - shadowcolor.a, 0.0, 1.0), 0.0, 1.0);
            }
            else
            {
                shading += shadow1;
            }
#else
            // Standard shadow accumulation
            shading += shadow0;
#endif
        }

        // Average the accumulated depth and shading over the number of iterations
        avgDepth *= inv_iterations;
        shading *= inv_iterations;

#ifdef COLORED_SHADOWS
        // Compute colored shadows scaled by ambient lighting
        colored = (shadowcolor.rgb * inv_iterations) * ambientA;
#endif
#ifdef CLOUD_SHADOWS
        // Apply cloud shadow factor to shading
        shading *= cloudShadow(p3, noise);
#endif

#if defined RSM
        // Calculate albedo if entity is not shadowed
        if (entity < 0.5)
            sAlbedo = calculateAlbedo(ProjShadowPos, noise_2, distortFac, ambientA, lightmap);
#endif

        // Replace pow(lightmap.y, 4.0) with direct multiplications for performance
        float lightmapY4 = lightmap.y * lightmap.y * lightmap.y * lightmap.y;
        if (sssAmount > 0.0)
            SSS = computeSSS(sssAmount, albedo, p3, lightPos, avgDepth, min(diffuseSun, shading)) * lightmapY4;
    }

#ifdef EXTENDED_SHADOWS
    // Handle extended shadows with additional calculations
    float mixamount = gbf(p3, shadowDistance);
    float SSS2 = 0.0;
    if (mixamount > 0.0)
    {
        float shading2 = rayTraceShadowAlt(diffuseSun, worldToView(lightPos), fragpos, noise, bool(sssAmount > 0));
        shading = mix(shading, shading2, mixamount);
        if (sssAmount > 0.0)
        {
            float distortFac = calcDistort(ProjShadowPos.xy);
            ProjShadowPos.xy *= distortFac;
            ProjShadowPos = ProjShadowPos * vec3(0.5, 0.5, 0.083333) + vec3(0.5);
            float avgDepth2 = max(ProjShadowPos.z - shading * 0.65, 0.0) ;
            float lightmapFactor = lightmap.y * lightmap.y * lightmap.y * lightmap.y;
            SSS2 = computeSSS(sssAmount, albedo, p3, lightPos, avgDepth2, diffuseSun) * lightmapFactor;
        }
        SSS = mix(SSS, SSS2, mixamount);
    }
#endif

    // Final shading adjustments based on diffuseSun and shading
    if (diffuseSun * shading > 0.001)
    {
#ifdef CONTACT_SHADOWS
        if (sssAmount == 0.0)
            shading = min(rayTraceShadow(diffuseSun, worldToView(lightPos), fragpos, noise), shading);
#endif
        shading = mix(0.0, shading, min(eyeBrightnessSmooth.y * recip + lightmap.y, 1.0));
    }

    // Clamp SSS and diffuseSun to the minimum of their current values and shading
    //    SSS = min(SSS, shading);
    diffuseSun = min(diffuseSun, shading);

    // Final blend of diffuseSun and SSS, factoring in shadow blend, lightmap, and rain strength
    diffuseSun = mix(diffuseSun * (1.0 - sssAmount), SSS, sssAmount) * shadowblend * lightmap.y * (1.0 - 0.9 * rainStrength);

    return diffuseSun;
}
