#include "/lib/fog/fog_common.glsl"
#include "/lib/fog/fogloop.glsl"
vec3 shadowSpace0()
{
    vec3 p3 = gbufferModelViewInverse[3].xyz;
    p3 = mat3(shadowModelView) * p3 + shadowModelView[3].xyz;
    p3 = diagonal3(shadowProjection) * p3 + shadowProjection[3].xyz;
    return p3;
}
vec3 blendTranslucencies(vec3 sceneColor, vec4 translucents)
{

    vec3 srcColor = translucents.rgb;
    float srcAlpha = translucents.a;

    // Destination color is the solid object in the framebuffer
    vec3 dstColor = sceneColor.rgb;

    vec3 temp = srcColor.rgb * 64.0;
    float lum = luma(temp);
    vec3 diff = clamp(temp - lum, 0, 1);
    if (translucents.a > 0 && isEyeInWater == 0)
    {

        // dstColor *= mix(diff, vec3(1.0), 0.75);
    }
    //  dstColor += srcColor;
    float dstAlpha = 1.0;

    vec3 finalColor = (srcColor * srcAlpha) + (dstColor * (1.0 - srcAlpha));

    return finalColor;
}
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;
}

vec3 getVolumetricRays3(
    vec3 solid,
    vec4 translucent,
    inout vec3 sky,
    float border,
    vec3 fragpos,
    vec3 fragpos0,
    bool iswater,
    float light,
    float skylight)
{
    // Initialize fog and noise parameters
    float estEyeDark = clamp((eyeBrightnessSmooth.y / 255.0), 0.0, 1.0);
    float estEyeDepth = clamp((14.0 - eyeBrightnessSmooth.y / 255.0 * 16.0) / 14.0, 0.0, 1.0);
    estEyeDepth *= estEyeDepth * estEyeDepth * 32.0;

    // Transform positions from view space to world space
    vec3 p31 = mat3(gbufferModelViewInverse) * fragpos + gbufferModelViewInverse[3].xyz;
    vec3 p30 = mat3(gbufferModelViewInverse) * fragpos0 + gbufferModelViewInverse[3].xyz;

    // Calculate distance and direction
    float Vdiff = distance(fragpos, fragpos0);
    float VdotU = normalize(p31).y;
    float estimatedDepth_a = Vdiff * mix(1.0, abs(VdotU), clamp(eyeBrightnessSmooth.y / 255.0, 0.0, 1.0));

    // Determine water status
    bool noWater = (isEyeInWater == 0 && !iswater);

    // Initialize volumetric lighting variables
    vec3 absorbance_w = vec3(1.0);
    vec3 vL_a = vec3(0.0);
    vec3 vL_w = vec3(0.0);
    float density = 0.0;
    float density2 = 0.0;

    // Shadow space calculations
    vec3 start = shadowSpace0();
    vec3 end = toShadowSpaceProjected(fragpos);
    vec3 refractedSunVec = refract(lightPos, -vec3(0.0, 1.0, 0.0), 0.9);

    // Ray length calculations
    float rayLength = length(fragpos);
    float rayLength0 = length(fragpos0);

    vec3 dV = end - start;
    vec3 dVWorld = mat3(gbufferModelViewInverse) * fragpos;

// Define ambient, sun color and fog based on environment
#ifdef NETHER
    vec3 ambient = vec3(fogColor);
    vec3 sunColor = vec3(fogColor);
    float fogAmount = 8.0;
#endif
#ifdef END
    vec3 ambient = vec3(fogColor) * 0.25;
    vec3 sunColor = vec3(fogColor);
    float fogAmount = 5.0;
#endif
#ifdef OVERWORLD
    vec3 ambient = atmosphereUp;
    vec3 sunColor = sunLight;
    float fogAmount = getFogDensity();
#endif
    float maxZ = (min(rayLength, 32.0) / (1e-8 + rayLength));

#ifdef OVERWORLD
    // Adjust ray length based on water conditions

    if (isEyeInWater == 1)
    {

        rayLength = length(fragpos) * maxZ;
        rayLength0 = length(fragpos0) * maxZ;
    }
    else
    {
        rayLength = gbf(fragpos, far, 0) * 1028.0;
        rayLength0 = gbf(fragpos0, far, 0) * 1028.0;

        if (iswater)
        {
            rayLength = mix(estimatedDepth_a, 14.0, gbf(fragpos0, far, 0));
            rayLength0 = rayLength;
        }
    }
#endif

    // Phase functions for scattering
    float dY = normalize(dVWorld).y * rayLength;
    float sh0 = 1.0;
    float rayLength2 = mix(rayLength, rayLength0, !iswater);

#ifdef VOLUMETRIC_LIGHTING
    // Volumetric lighting calculations
    AtmosphereLoop(
        rayLength2,
        start,
        dV * maxZ,
        dY,
        estEyeDepth,
        refractedSunVec,
        ambient,
        sunColor,
        sh0,
        absorbance_w,
        vL_w,
        density,
        p31,
        fogAmount);
#else
    waterNoLoop(
        rayLength2,
        dY,
        estEyeDepth,
        refractedSunVec,
        ambient,
        sunColor,
        absorbance_w,
        vL_w,
        p31);
    density = fogAmount;
#endif

    density2 = (density * 256.0 + fogAmount * 6.0);

#ifndef VL_NOISE_DENSITY
    density2 = (fogAmount * 6.0);
    density = 0.0;
#endif

    vec3 transmittance;

    // Atmospheric lighting calculations
    float rayLengthGround0 = pow(length(fragpos0), FOG_FALLOFF) * density2;
    float rayLengthGround1 = pow(length(fragpos), FOG_FALLOFF) * density2;

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

    if (isEyeInWater <= 1)
    {
        applySkyBlend2(vL_a0, sky, p30, far);
        applySkyBlend2(vL_a1, sky, p31, far);
        //    if (isEyeInWater == 0)
        //        applySkyBlend2(vL_w, sky, p31, far);
    }

#ifdef VL_NOISE_DENSITY
    vL_a = (!iswater || isEyeInWater == 1) ? mix(vL_a0, vL_a1, translucent.a) : vL_a1;
#else
    vL_a = (!iswater || isEyeInWater == 1) ? mix(vL_a0, vL_a1, translucent.a) * sh0 : vL_a1;
#endif
    vL_a *= estEyeDark;
    if (isEyeInWater == 0)
        vL_w *= estEyeDark;
    // Apply final effects

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

    if (isEyeInWater <= 1)
    {
        applySkyBlend(solid, translucent, sky, vL_w, p31, p30, far, iswater);
    }

    // Combine solid and translucent colors
    vec3 color = blendTranslucencies(solid, translucent);

    return color;
}
