uniform sampler2D depthtex1;
uniform sampler2D gaux2;
uniform sampler2D gaux3;
uniform sampler2D gaux4;
uniform sampler2D gaux1;

float speculars(vec3 n, vec3 rd, vec3 l, float km, float f0)
{
    // km = max(pow(km, 2.0), 0.0001);

    vec3 h = normalize(l - rd);
    float ndh = dot(n, h);
    float g = ndh * ndh * (km * km - 1.0) + 1.0;
    float ggx = km * km / (PI * g * g);

    float fre = 1.0 + dot(rd, n);

    float kr = f0 + (1.0 - f0) * (1. - km) * (1.0 - km) * pow(fre, 5.0);

    return kr * ggx;
}
vec3 specularWater(vec3 n, vec3 rd)
{
    // Hardcoded values for demonstration, adjust as needed
    vec3 f0 = vec3(0.2); // Typical value for non-metallic surfaces
    float km = 0.5;      // Example roughness squared
    vec3 light = toScreenSpace(vec3(0, -1, 0));

    vec3 h = normalize(light - rd);
    float ndh = dot(n, h);
    float g = ndh * ndh * (km * km - 1.0) + 1.0;
    float ggx = km * km / (PI * g * g);

    float fre = 1.0 + dot(rd, n);

    vec3 kr = f0 + (1.0 - f0) * (1. - km) * (1.0 - km) * pow(fre, 5.0);

    return kr * ggx;
}
float fresnel0(vec3 l, vec3 n, float eta1, float eta2)
{
    float cosThetaI = abs(dot(l, n)); // cos(theta_i) - angle of incidence
    float eta = eta2 / eta1;          // relative index of refraction
    float sin2ThetaI = 1.0 - cosThetaI * cosThetaI;
    float sin2ThetaT = eta * eta * sin2ThetaI; // sin^2(theta_t) using Snell's law

    float cosThetaT = sqrt(max(0.0, 1.0 - sin2ThetaT)); // cos(theta_t)

    // Reflectance for s-polarized light
    float Rs = (eta1 * cosThetaI - eta2 * cosThetaT) / (eta1 * cosThetaI + eta2 * cosThetaT);
    Rs = Rs * Rs;

    // Reflectance for p-polarized light
    float Rp = (eta2 * cosThetaI - eta1 * cosThetaT) / (eta2 * cosThetaI + eta1 * cosThetaT);
    Rp = Rp * Rp;

    // Average of the s and p polarization reflectances
    return 0.5 * (Rs + Rp);
}

/*

        float currentDither =
            fract(0.75487765 * gl_FragCoord.x + 0.56984026 * gl_FragCoord.y + 1 / 1.6180339887 * (i + frameCounter));

        vec3 spos = clipPosition + stepv * (float(i) + currentDither);

*/

vec3 rayTrace(vec3 dir, vec3 position, float dither)
{
    const float quality = SSR_STEPS;

    vec3 clipPosition = toClipSpace3(position);
    vec3 direction = normalize(toClipSpace3(position + dir) - clipPosition);
    vec3 maxLengths = (step(0.0, direction) - clipPosition) / direction;
    float minLength = min(min(maxLengths.x, maxLengths.y), maxLengths.z);
    vec3 stepv = (direction * minLength) / quality;

    float tolerance = max(abs(stepv.z) * 4.0, 0.02 / (position.z * position.z));

    for (int i = 0; i <= int(quality); ++i)
    {

        vec3 spos = clipPosition + stepv * (i + dither);

        float depthSample = texelFetch2D(depthtex1, ivec2(spos.xy / texelSize), 0).r;
        if (depthSample < spos.z && abs(spos.z - depthSample) < tolerance)
        {
            return vec3(spos.xy, depthSample);
        }
    }

    return vec3(1.1); // Indicates no intersection found
}

vec3 reflections(vec3 direct, float fresnel, float sunSpec, float skyLight, vec3 normal, float watermult, vec3 fragpos)
{

    vec3 reflectedVector = reflect(normalize(fragpos), normal);
    vec3 reflection = vec3(0.0);
    vec4 tempreflection = vec4(0.0);

    if (pow(skyLight, 3) > 0.0)
    {
        vec3 wrefl = mat3(gbufferModelViewInverse) * reflectedVector;

        wrefl.y *= watermult;

        vec4 sky = skyFromTex2((wrefl), gaux3);
        reflection.rgb = (sky.rgb + (sky.a * direct * 0.2)) * pow(skyLight, 2);
    }

    vec3 rtPos = rayTrace(reflectedVector, fragpos.xyz, R2_dither(gl_FragCoord.xy));

    vec3 previousPosition =
        reproject(mat3(gbufferModelViewInverse) * toScreenSpace(rtPos) + gbufferModelViewInverse[3].xyz, false);
    if (all(greaterThanEqual(previousPosition.xy, vec2(0.0))) && rtPos.z < 1.1)
    {

        tempreflection = vec4(texture(gaux1, previousPosition.xy).rgb, 1);
    }

    if (isEyeInWater == 1)
    {

        reflection = mix(
            mix(min(specularWater((normal), normalize(fragpos)), 16.0) * vec3(0.1, 0.4, 0.5) * 1, vec3(0), 1 - fresnel),
            tempreflection.rgb, tempreflection.a);
    }
    else
    {
        reflection = mix(reflection.rgb, tempreflection.rgb, tempreflection.a) * fresnel;
    }
    reflection = (reflection.rgb) + sunSpec * direct;

    return reflection;
}

//////////
vec3 waterNormal(vec2 coord, vec3 normFragpos)
{
    coord = mod(coord / 64.0, 1.0);
    float strength = 1.0;

    float lod = clamp((pow(1.0 + dot(normFragpos, normal0), 4)), 0, 1);

    float deltaPos = 0.0012;
    deltaPos = mix(deltaPos, 0.0, lod * 1.0);
    strength = mix(strength, 0.0, lod * 0.9);

    vec2 offsets[4] = vec2[](vec2(deltaPos, 0.0), vec2(-deltaPos, 0.0), vec2(0.0, deltaPos), vec2(0.0, -deltaPos));

    float heights[4];
    for (int i = 0; i < 4; i++)
    {
        heights[i] = texture(gaux4, coord + offsets[i]).x;
    }

    float dx = (heights[1] - heights[0]) * strength;
    float dy = (heights[3] - heights[2]) * strength;
    vec3 full = normalize(vec3(dx, dy, 0.5)) + 0.5;

    return full;
}