#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
// ---------------------------------

#define SHADOW_DISTORT_FACTOR 0.1
varying vec2 texcoord;




vec3 GetShadowColor(vec3 shadowCoord) {
    // If the shadow coordinate is outside the map (0.0 to 1.0), 
    // it means this point is outside the shadow distance.
    // We return 1.0 (full sun) to avoid glitches at the edge.
    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);

    // 3x3 Loop for Softness
    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(depthtex0, texcoord).r;

    // --- DH Logic: Check if we hit vanilla terrain or DH terrain ---
    bool isDH = false;
    
    #ifdef DISTANT_HORIZONS
    // If vanilla depth is 1.0 (sky/void), check DH depth
    float depthDH = texture2D(dhDepthTex0, texcoord).r;
    if (depth >= 1.0 && depthDH < 1.0) {
        depth = depthDH;
        isDH = true;
    }
    #endif

    // Only process if we hit something (Vanilla OR DH)
    if (depth < 1.0) {
        // --- Coordinate Math ---
        vec4 clipPos = vec4(texcoord * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
        vec4 viewPos;
        
        // Use the correct projection inverse depending on what we hit
        #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;

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

        // --- SHADOW INTENSITY TWEAKS ---
        // 1. Calculate how much "Shadow" to actually apply.
        // We use a small multiplier (0.1) on skyLight to make shadows darker. 
        // Lower 0.1 to 0.05 for pitch black shadows, or raise it to 0.2 for lighter ones.
        vec3 shadowFloor = vec3((blockLight)); 

        // 2. Get your raw shadow samples
        vec3 incomingLight = GetShadowColor(shadowCoords);

        // --- NIGHT SHADOW LOGIC ---
        // Instead of forcing it to 1.0 (white), mix based on nightStrength.

        float nightShadowFactor = 0.4; 
        float shadowPower = mix(1.0, nightShadowFactor, nightStrength);

        // Apply the floor and the strength
        // This ensures that even at night, shadows exist but are influenced by moon/block light.
        vec3 finalShadow = max(incomingLight, shadowFloor * 0.5);
        finalShadow = mix(vec3(1.0), finalShadow, shadowPower); 

        // --- LIGHTING CALCULATION ---
        const vec3 SUN_COLOR = vec3(1.6, 1.0, 0.6);
        const vec3 MOON_COLOR = vec3(0.820, 0.761, 0.647); 

        // Mix the light color based on time
        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
        
        // Your existing ambient logic
        vec3 dayShadow = vec3(0.15, 0.15, 0.35); 
        vec3 nightLight = vec3(0.40, 0.40, 0.65); 
        float timeMix = clamp(timeWeight, 0.0, 1.0);
        vec3 ambientColor = mix(nightLight, dayShadow, timeMix); 

        Color *= (ambientColor + finalSunlight);
        
        // --- Grading & Exposure ---
        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);
    }

    gl_FragColor = vec4(Color, 1.0);
}