// ============================================================================
// WATER RENDERING AND UNDERWATER FOG
// ============================================================================
#ifndef WATER_GLSL
#define WATER_GLSL

#include "/lib/settings.glsl"
#include "/lib/sky.glsl"
#include "/lib/noises.glsl"

uniform ivec2 eyeBrightnessSmooth;

// Fetch a single noise texture based on its ID.
float get_water_noise_tex(int type, vec2 uv) {
    if (type == 1) return texture2DLod(noisetex1, uv, 0.0).r;
    if (type == 2) return texture2DLod(noisetex2, uv, 0.0).r;
    if (type == 3) return texture2DLod(noisetex3, uv, 0.0).r;
    if (type == 4) return texture2DLod(noisetex4, uv, 0.0).r;
    if (type == 5) return texture2DLod(noisetex5, uv, 0.0).r;
    return texture2DLod(noisetex6, uv, 0.0).r;
}

// Mix two noises with independent scales for water sparkles.
float get_sparkle_noise(vec2 uv) {
    float n1 = get_water_noise_tex(WATER_SPARKLE_NOISE_1, uv * float(WATER_SPARKLE_SCALE_1));
    // Offset the second UV slightly.
    vec2 uvB = (uv * 1.31) + vec2(0.14, 0.68); 
    float n2 = get_water_noise_tex(WATER_SPARKLE_NOISE_2, uvB * float(WATER_SPARKLE_SCALE_2));
    
    float mix_val = float(WATER_SPARKLE_MIX);
    
    // Standard crossfade.
    float blended = mix(n1, n2, mix_val);
    
    // Dynamic contrast restoration: peaks at 1.0 when mix_val is exactly 0.5.
    float contrast_boost = 1.0 + (1.0 - abs(mix_val * 2.0 - 1.0)) * 1.2; 
    
    // Apply contrast and clamp to prevent artifacts.
    return clamp((blended - 0.5) * contrast_boost + 0.5, 0.0, 1.0);
}

// Mix two noises with independent scales for water shores.
float get_shore_noise(vec2 uv) {
    float n1 = get_water_noise_tex(WATER_SHORE_NOISE_1, uv * float(WATER_SHORE_SCALE_1));
    // Offset the second UV slightly.
    vec2 uvB = (uv * 1.31) + vec2(0.14, 0.68);
    float n2 = get_water_noise_tex(WATER_SHORE_NOISE_2, uvB * float(WATER_SHORE_SCALE_2));
    
    return mix(n1, n2, float(WATER_SHORE_MIX));
}

// Underwater camera fog and global illumination.
void applyUnderwaterFog(
    float distance, vec3 worldPos, vec3 worldViewDir, 
    vec3 worldSunDir, float skyLightCurve, float rainStrength, vec3 lightSunColor, 
    out vec3 finalFogColor, out float finalFogFactor
) {
    // 1. Calculate volumetric underwater fog using specific density and falloff.
    float density = float(UNDERWATER_FOG_DENSITY) * float(WATER_A);
    finalFogFactor = clamp(exp(-pow(distance * density, float(UNDERWATER_FOG_FALLOFF))), 0.0, 1.0);

    vec3 zenithCol, horizonCol;
    getSkyColors(vec3(0.0, 1.0, 0.0), worldSunDir, rainStrength, zenithCol, horizonCol);
    
    // Average sky color for ambient underwater lighting.
    vec3 ambientSky = mix(horizonCol, zenithCol, 0.5);
    
    // Generate the cave base ambient color
    vec3 caveAmbient = vec3(CAVE_FOG_R, CAVE_FOG_G, CAVE_FOG_B) * float(MIN_CAVE_LIGHT);
    
    // Get the REAL exposure of the player's camera
    float cameraSkyLight = clamp(float(eyeBrightnessSmooth.y) / 240.0, 0.0, 1.0);
    
    // 1. On utilise une courbe beaucoup plus douce pour la transition de la caméra
    float smoothCameraExposure = pow(cameraSkyLight, 1.2);
    
    // 2. Fondu magique : l'eau proche utilise sa vraie lumière (skyLightCurve), 
    // mais le brouillard lointain écoute la caméra pour boucher les trous de lumière.
    float distanceFade = exp(-distance * 0.05);
    float finalExposure = mix(smoothCameraExposure, max(skyLightCurve, smoothCameraExposure), distanceFade);
    
    // Transition ultra fluide
    vec3 contextualAmbient = mix(caveAmbient, ambientSky, finalExposure);
    
    vec3 baseColor = vec3(WATER_R, WATER_G, WATER_B);
    baseColor *= max(contextualAmbient * 2.5, vec3(0.005));
    
    float absoluteDepth = max(62.0 - worldPos.y, 0.0);
    float depthDarkening = clamp(absoluteDepth / 32.0, 0.0, 1.0);
    vec3 depthColor = mix(baseColor, baseColor * float(UNDERWATER_DEPTH_BRIGHTNESS), depthDarkening);
    
    float viewGradient = clamp(worldViewDir.y * 0.5 + 0.5, 0.0, 1.0);
    vec3 viewColor = mix(depthColor * float(UNDERWATER_BOTTOM_MULTIPLIER), depthColor * float(UNDERWATER_HORIZON_BRIGHTNESS), viewGradient);
    
    // Directional godrays and surface glow (safeguarded)
    float safeSkyCurve = min(skyLightCurve, finalExposure * 2.0 + 0.1);
    float sunDot = max(dot(worldViewDir, worldSunDir), 0.0);
    float sunGlow = pow(sunDot, 4.0) * safeSkyCurve;
    float surfaceGlow = max(worldViewDir.y, 0.0) * safeSkyCurve;
    
    viewColor += lightSunColor * (sunGlow * 0.6 + surfaceGlow * 0.2) * (1.0 - finalFogFactor);
    finalFogColor = viewColor;
}

float linearizeWaterDepth(float d, float near, float far) {
    return (2.0 * near * far) / (far + near - (2.0 * d - 1.0) * (far - near));
}

// Apply water surface material properties (Called by gbuffers_water).
void applyWaterMaterial(
    inout vec4 albedo, inout vec3 outputNormal, out float fresnel, out float sparklesOut, // Added sparklesOut
    sampler2D waterTex, sampler2D depthtex1, 
    vec2 screenUV, float fragZ, vec3 worldPos, vec3 normal, vec4 tangent, vec3 viewDir, 
	vec3 worldSunDir,
    float frameTimeCounter, float near, float far
) {
    sparklesOut = 0.0; // Initialize output variable
    #if VANILLA_WATER_TEXTURE == 0
        albedo = vec4(WATER_R, WATER_G, WATER_B, WATER_A);
    #endif
    
    // Depth-based darkening.
    float terrainDepth = texture2D(depthtex1, screenUV).r;
    float linTerrain = linearizeWaterDepth(terrainDepth, near, far);
    float linWater = linearizeWaterDepth(fragZ, near, far);
    float depthDiff = max(linTerrain - linWater, 0.0);
    float depthFactor = clamp(depthDiff / 8.0, 0.0, 1.0);
    float depthShading = mix(WATER_SURFACE_DARKNESS, WATER_DEPTH_BRIGHTNESS, depthFactor);
    albedo.rgb *= depthShading;
    albedo.a = mix(albedo.a * 0.7, albedo.a, clamp(depthDiff / 2.0, 0.0, 1.0));
    
    // Animated surface normals using dual texture fetching.
    vec3 n = normalize(normal);
    vec3 bitangent = cross(tangent.xyz, n) * tangent.w;
    mat3 tbn = mat3(tangent.xyz, bitangent, n);
    
    float timeMove = frameTimeCounter * 0.5 * float(WATER_SPEED);
    vec2 uv1 = worldPos.xz * 0.7 * float(WATER_SIZE) + timeMove;
    vec2 uv2 = worldPos.xz * 1.0 * float(WATER_SIZE) - timeMove;
    
    vec4 normalTex1 = texture2D(waterTex, uv1);
    vec4 normalTex2 = texture2D(waterTex, uv2);
    vec4 normalTex = mix(normalTex1, normalTex2, 0.5);
    normalTex.xy = mix(vec2(0.5), normalTex.xy, float(WATER_NORMALS));
    normalTex.xy = normalTex.xy * 2.0 - 1.0;
    normalTex.z = sqrt(1.0 - dot(normalTex.xy, normalTex.xy));
    outputNormal = normalize(tbn * normalTex.xyz);

    // Calculate stylized wave shading.
    float waveValue = (normalTex.x + normalTex.y); // Raw direction data from normal map.
    
    // Apply "Color Step" / Hardness logic.
    float hardness = float(WATER_WAVE_SHADING_HARDNESS);
    if (hardness > 0.01) {
        // High hardness creates distinct bands of light and shadow.
        waveValue = smoothstep(-0.1 - (1.0 - hardness), 0.1 + (1.0 - hardness), waveValue);
    }

    float finalShading = waveValue * float(WATER_WAVE_SHADING_STRENGTH);
    
    albedo.rgb += max(finalShading, 0.0); // Light peaks.
    albedo.rgb -= max(-finalShading, 0.0) * 0.5; // Darker troughs.

    // Fresnel reflection approximation.
    fresnel = pow(clamp(1.0 + dot(outputNormal, viewDir), 0.0, 1.0), float(FRESNEL_EXPONENT));
    
    #if WATER_SPARKLES == 1
        vec2 sparkleCoord = worldPos.xz * 0.01;
        sparkleCoord += normalTex.xy * float(WATER_SPARKLE_DISTORTION); 
        
        float brightEdge = get_sparkle_noise(sparkleCoord);
        float fracture = get_sparkle_noise(sparkleCoord * 3.0);
        brightEdge = clamp(brightEdge - (fracture * float(WATER_SPARKLE_FRACTURE) * 0.5), 0.0, 1.0);
        
        float threshold = 1.0 - (float(WATER_SPARKLE_DENSITY) * 0.95);
        float densityMask = smoothstep(threshold, threshold + float(WATER_SPARKLE_SMOOTHNESS), brightEdge);
        float sharpSparkles = pow(brightEdge, 6.0);

        // Compute night factor to dim sparkles.
        bool isSunrise = checkIsSunrise();
        float sVis, nightVis, oVis;
        getTransitionFactor(worldSunDir.y, isSunrise, sVis, nightVis, oVis);
        float nightDimmer = mix(1.0, float(WATER_SPARKLE_NIGHT_MULTIPLIER), nightVis);

        // Apply night dimmer to the final sparkle intensity.
        float sparkles = sharpSparkles * densityMask * nightDimmer;

        // --- Sun/Moon Reflection Influence ---
        // Convert view and normal to world space to calculate the reflection zone.
        vec3 worldViewDir = normalize(mat3(gbufferModelViewInverse) * viewDir);
        vec3 worldNormal = normalize(mat3(gbufferModelViewInverse) * outputNormal);
        
        // Identify the active celestial body (Sun or Moon).
        vec3 activeLightDir = worldSunDir.y > 0.0 ? worldSunDir : -worldSunDir;
        
        // Calculate the specular reflection mask.
        float sunMask = max(dot(reflect(worldViewDir, worldNormal), activeLightDir), 0.0);
        sunMask = pow(sunMask, 12.0); // Tighten the area around the reflection.
        
        // Blend between global sparkles and localized sparkles.
        float finalInfluence = mix(1.0, sunMask, float(WATER_SPARKLE_SUN_INFLUENCE));
        sparkles *= finalInfluence;

        albedo.rgb += sparkles * float(WATER_SPARKLE_STRENGTH);
        sparklesOut = sparkles;
    #endif
	
    #if WATER_SHORE == 1
        // Stabilize depth to prevent foam crushing near shores.
        float rigidDepth = sqrt(depthDiff);
        float shoreGradient = 1.0 - clamp(rigidDepth / float(WATER_SHORE_DISTANCE), 0.0, 1.0);
        
        vec2 shoreCoord = worldPos.xz * 0.01;
        // Distort shore UVs using the normal map and the new slider.
        shoreCoord += normalTex.xy * float(WATER_SHORE_DISTORTION);
        
        float shoreNoise = get_shore_noise(shoreCoord);

        // Hard-edge anime style rendering via tight smoothstep.
        float foam = smoothstep(0.49, 0.51, shoreGradient * shoreNoise * 1.5);
        
        // Force foam to zero in deep water to prevent sparkle interference.
        foam *= step(depthDiff, float(WATER_SHORE_DISTANCE) * 4.0);
        float contactLine = smoothstep(0.89, 0.91, shoreGradient);
        foam = max(foam, contactLine);

        albedo.rgb = mix(albedo.rgb, vec3(1.0), foam * float(WATER_SHORE_OPACITY));
        // Increase alpha based on foam density to ensure solid white shores.
        albedo.a = mix(albedo.a, 1.0, foam * float(WATER_SHORE_OPACITY)); 
    #endif
}

#endif