#include "/lib/all_the_libs.glsl"
#include "/lib/distort.glsl"

uniform sampler2D gcolor;
uniform sampler2D lightmap;



// --- Distant Horizons Uniforms ---
#ifdef DISTANT_HORIZONS
uniform sampler2D dhDepthTex0;
#endif

varying vec2 texcoord;





float get_nostalgic_caustics(vec3 worldPos) {
    vec2 p = mod(worldPos.xz, 1000.0) * 1.5; 
    float t = frameTimeCounter * 0.4;

    p.x += sin(p.y * 1.2 + t) * 0.5;
    p.y += sin(p.x * 1.2 + t) * 0.5;

    // Standard Caustic Layer
    float grid1 = sin(p.x * 2.5 + t) + sin(p.y * 2.5 - t);
    float grid2 = sin(p.x * 5.0 - t) + sin(p.y * 5.0 + t);
    float baseCaustics = 1.0 - abs((grid1 + grid2 * 0.2) * 0.4);
    baseCaustics = pow(max(0.0, baseCaustics), 4.0);

    // --- HOTSPOT LAYER ---
    // A much faster, sharper grid to create "sparkles" at intersections
    float sparkleGrid = sin(p.x * 6.0 + t * 2.0) * sin(p.y * 6.0 - t * 1.5);
    float hotspots = pow(max(0.0, sparkleGrid), 16.0) * 2.5; 

    // Combine: The hotspots live "inside" the existing caustic lines
    return baseCaustics + (hotspots * baseCaustics);
}

// --- UTILS ---

float check_is_underwater(vec2 texcoord) {
    float depthAll = texture2D(depthtex0, texcoord).r;
    float depthSolid = texture2D(depthtex1, texcoord).r;
    if (depthAll < depthSolid - 0.0001) return 1.0;
    if (isEyeInWater == 1 && depthAll > depthSolid - 0.0001) return 1.0;
    return 0.0;
}

vec3 GetShadowColor(vec3 shadowCoord) {
    if (shadowCoord.x < 0.0 || shadowCoord.x > 1.0 || 
        shadowCoord.y < 0.0 || shadowCoord.y > 1.0 || 
        shadowCoord.z < 0.0 || shadowCoord.z > 1.0) {
        return vec3(1.0);
    }
    float pixelSize = 1.0 / SHADOW_MAP_RESOLUTION; 
    float bias = computeBias(shadowCoord);
    vec3 shadowTotal = vec3(0.0);
    for(int x = -1; x <= 1; x++) {
        for(int y = -1; y <= 1; y++) {
            vec2 offset = vec2(x, y) * pixelSize;
            vec2 sampleCoord = shadowCoord.xy + offset;
            float depthAll = texture2D(shadowtex0, sampleCoord).r;
            float isBlockedByAnything = step(depthAll, shadowCoord.z - bias);
            if (isBlockedByAnything == 0.0) {
                shadowTotal += vec3(1.0);
                continue; 
            }
            float depthOpaque = texture2D(shadowtex1, sampleCoord).r;
            float isBlockedByOpaque = step(depthOpaque, shadowCoord.z - bias);
            if (isBlockedByOpaque == 1.0) {
                shadowTotal += vec3(0.0);
            } else {
                vec4 tint = texture2D(shadowcolor0, sampleCoord);
                shadowTotal += tint.rgb;    
            }
        }
    }
    return shadowTotal / 9.0;
}

void main() {
    vec3 Color = texture2D(colortex0, texcoord).rgb;
    float depth = texture2D(depthtex1, texcoord).r;

    bool isDH = false;
    #ifdef DISTANT_HORIZONS
    float depthDH = texture2D(dhDepthTex1, texcoord).r;
    if (depth >= 1.0 && depthDH < 1.0) {
        depth = depthDH;
        isDH = true;
    }
    #endif

     vec4 clipPos = vec4(texcoord * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
        vec4 viewPos;
        
        #ifdef DISTANT_HORIZONS
        if (isDH) {
            viewPos = dhProjectionInverse * clipPos;
        } else {
            viewPos = gbufferProjectionInverse * clipPos;
        }
        #else
        viewPos = gbufferProjectionInverse * clipPos;
        #endif

        viewPos /= viewPos.w;
        vec4 worldPos = gbufferModelViewInverse * viewPos;

        vec4 shadowPos = shadowModelView * worldPos;
        shadowPos = shadowProjection * shadowPos;
        vec3 distortedPos = distort(shadowPos.xyz);
        vec3 shadowCoords = distortedPos * 0.5 + 0.5;

    if (depth < 1.0) {
       

        vec2 lmCoord = texture2D(lightmap, texcoord).rg;
        float blockLight = lmCoord.x;

        vec3 shadowFloor = vec3((blockLight)); 
        vec3 incomingLight = GetShadowColor(shadowCoords);

        float nightShadowFactor = 0.4; 
        float shadowPower = mix(1.0, nightShadowFactor, nightStrength);
        vec3 finalShadow = max(incomingLight, shadowFloor * 0.5);
        finalShadow = mix(vec3(1.0), finalShadow, shadowPower); 

        const vec3 SUN_COLOR = vec3(1.6, 1.0, 0.6) * SUN_COLOR_MULT;
        const vec3 MOON_COLOR = vec3(0.820, 0.761, 0.647); 
        vec3 currentLightColor = mix(SUN_COLOR, MOON_COLOR, nightStrength);
        float timeWeight = dayStrength + sunriseStrength + sunsetStrength + (nightStrength * 0.5);

        #ifdef GODRAYS
            vec3 finalSunlight = currentLightColor * finalShadow * (1.0-thunderStrength);
        #else
            vec3 finalSunlight = currentLightColor * finalShadow * timeWeight * (1.0-thunderStrength);
        #endif
        
        vec3 dayShadow = vec3(0.05, 0.05, 0.15); 
        vec3 nightLight = vec3(1.20, 1.20, 1.35); 
        float timeMix = clamp(timeWeight, 0.0, 1.0);
        vec3 ambientColor = mix(nightLight, dayShadow, timeMix); 

        Color *= (ambientColor + finalSunlight);
        
        float gray = dot(Color, vec3(0.299, 0.587, 0.114));
        Color = mix(vec3(gray), Color, 1.15); 
        
        Color *= 0.6; 
        Color *= vec3(1.05, 1.0, 0.95);

    }

 #ifdef WATER_CAUSTICS
        // --- CAUSTIC LOGIC ---
        float isUnderwater = check_is_underwater(texcoord);
        float isSunlit = GetShadowColor(shadowCoords).r;

        // NEW: Grab the color of the transparent layer (water/glass)
        // Water is blue/cyan; white glass is neutral.
        vec3 transColor = texture2D(colortex0, texcoord).rgb;
        bool isWater = (transColor.b > transColor.r + 0.05); // Simple color mask

        // We only run this if it's actually water OR if your head is already inside it
        if (isUnderwater > 0.5 && isSunlit > 0.1 && (isWater || isEyeInWater == 1)) {
            
            // 1. Get Absolute Floor Position
            float floorDepth = texture2D(depthtex1, texcoord).r;
            vec4 cPos = vec4(texcoord * 2.0 - 1.0, floorDepth * 2.0 - 1.0, 1.0);
            vec4 vPos = gbufferProjectionInverse * cPos;
            vPos /= vPos.w; 
            vec4 floorPos = gbufferModelViewInverse * vPos; 

            // 2. Add Camera Position (Locks texture to world)
            vec3 absoluteWorldPos = floorPos.xyz + cameraPosition;

            float rPattern = get_nostalgic_caustics(absoluteWorldPos + 0.04);
            float gPattern = get_nostalgic_caustics(absoluteWorldPos);
            float bPattern = get_nostalgic_caustics(absoluteWorldPos - 0.04);
            
            vec3 causticColor;
            causticColor.r = rPattern * 1.1; 
            causticColor.g = gPattern;
            causticColor.b = bPattern * 1.2; 

            vec3 sunTint = vec3(1.0, 0.9, 0.7); 
            vec3 finalCaustic = causticColor * sunTint;

            // Keep your original intensity and over-exposure logic
            float intensity = (dayStrength + 0.001) * CAUSTICS_STRENGTH * (1-rainStrength);
            Color.rgb += finalCaustic * isSunlit * intensity;

            if (gPattern > 0.8) {
                Color.rgb += vec3(0.1, 0.08, 0.05) * gPattern * isSunlit;
            }
        }
    #endif

    gl_FragColor = vec4(Color, 1.0);
}