#include "/lib/ssr/ssr_common.glsl"

#include "/lib/water/rt.glsl"

#include "/lib/specular.glsl"

vec4 calculateSpecularLightingLQ(
    vec3 normal,
    vec3 np3,
    float shading,
    float skylight,
    vec3 fragpos,
    vec4 scene,
    float alpha0)
{
    const float f0 = 0.035;
    bool inWater = (isEyeInWater == 1);

    // Compute specular term with the GGX distribution
    vec3 specTerm = shading * GGX2(normal, -np3, lightPos, WATER_ROUGHNESS, vec3(f0));

    // Define noise based on SSR_STEPS:
#if SSR_STEPS > 3
    float noise = R2_dither(gl_FragCoord.xy);
#else
    const float noise = 0.5;
#endif

    // Reflection vector rotated around the X-axis
    vec3 L = rotateAroundXAxis(reflect(np3, normal));

    // Compute Fresnel term and geometric shadowing (G1)
    const float etaAir = 1.0;
    const float etaWater = 1.333;
    float eta = mix(etaAir, etaWater, inWater);

    // Cosine of incidence angle
    float cosThetaI = clamp(dot(np3, normal), -1.0, 1.0);
    float absCosTheta = abs(cosThetaI);

    // Calculate sin²θ_t for total internal reflection
    float sinThetaT2 = (1.0 - cosThetaI * cosThetaI) * (eta * eta);

    // TIR mask without step()
    float tir = clamp((sinThetaT2 - 1.0) * 1e6 + 0.5, 0.0, 1.0); // Smooth transition from 0 to 1

    // Schlick's approximation for Fresnel term
    float F = mix(f0 + (1.0 - f0) * pow(1.0 - absCosTheta, 5.0), 1.0, tir * clamp(iswater, 0.0, 1.0));

    // Geometric shadowing calculation
    float NoL = clamp(dot(normal, L), 0.0, 1.0);
    float eps = fwidth(NoL);
    const float k0 = 0.99995;
    const float k1 = 0.00005;
    float G1 = NoL / (NoL * k0 + k1 + eps);

    // Combine Fresnel and geometric terms
    float rayContrib = F * G1;

    // Sample sky color and add sun light contribution
    vec4 sky = skyFromTexLod(L, colortex6, 1.5);
    sky.rgb = mix(sky.rgb, vec3(sky.a * sunLight), clamp(sky.a, 0.0, 1.0));

    // Reflection color calculation
    vec3 reflectionColor = mix(scene.rgb, sky.rgb, skylight);

    // Screen-space reflections (SSR)
#if SSR_STEPS > 0
    vec3 rtPos;
    #ifdef DISTANT_HORIZONS
    rtPos = RT_SIMPLE_DH(mat3(gbufferModelView) * L, fragpos, WATER_ROUGHNESS, noise, SSR_STEPS);
    #else
    rtPos = RT_SIMPLE(mat3(gbufferModelView) * L, fragpos, WATER_ROUGHNESS, noise, SSR_STEPS);
    #endif

    // Check valid ray depth in SSR
    bool validRtPos =
    #ifdef DISTANT_HORIZONS
        (rtPos.z < 1.0 && rtPos.z > 0.0);
    #else
        (rtPos.z < 1.0);
    #endif

    if (validRtPos)
    {
        vec2 previousUV;
    #ifdef DISTANT_HORIZONS
        previousUV = ReprojectDH(rtPos).xy;
    #else
        previousUV = Reproject(rtPos).xy;
    #endif

        vec2 texcoord = gl_FragCoord.xy * viewResolution;
        vec2 delta = texcoord - rtPos.xy;

        if (dot(delta, delta) > 0.0001 &&
            all(greaterThan(previousUV, vec2(0.0))) &&
            all(lessThan(previousUV, vec2(1.0))))
        {
            reflectionColor = texture(colortex4, previousUV, -2.0).rgb;
        }
    }
#endif

    // Final composition of the specular lighting
    vec4 final = mergeSpecularLighting(
        reflectionColor,
        vec3(rayContrib),
        specTerm,
        vec3(1.0),
        sunLight,
        scene,
        alpha0,
        inWater);

    return final;
}
vec4 reflections(float skyLight, vec3 normal, vec3 fragpos, vec4 materialProps, vec4 outcolor, float shading, vec3 albedo, float alpha0)
{

    vec3 p3 = mat3(gbufferModelViewInverse) * fragpos;
    vec4 reflection = calculateSpecularLightingLQ(normal, normalize(p3), shading, skyLight, fragpos, outcolor, alpha0);

    return reflection;
}

//////////
