#version 410 core
in vec2 vUV;
flat in float vLayer;
in float vShade;
in float vDist;
in vec3 vWorld;
flat in float vLiquid;

uniform sampler2DArray uAtlas;
uniform float uAlphaCutoff; // 0.5 cutout, 0 opaque-only
uniform vec3  uFogColor;
uniform float uFogStart;
uniform float uFogEnd;
uniform int   uFogMode;          // 0 linear, 1 exp2 + height falloff
uniform float uFogDensity;       // exp2 mode: tuned so fog ~saturates at uFogEnd
uniform float uFogHeightFalloff; // exp2 mode: thins with altitude above sea level
uniform float uTime;             // shared with the vertex clock
uniform float uWaveAmp;          // >0 = fancy water (UV flow + fresnel)
uniform vec3  uCamPos;
uniform int   uDynLightCount;
uniform vec4  uDynLightPosRadius0;
uniform vec4  uDynLightPosRadius1;
uniform vec4  uDynLightPosRadius2;
uniform vec4  uDynLightPosRadius3;
uniform vec4  uDynLightColor0;
uniform vec4  uDynLightColor1;
uniform vec4  uDynLightColor2;
uniform vec4  uDynLightColor3;

out vec4 FragColor;

vec3 pointLight(vec3 p, vec4 posRadius, vec4 colorIntensity) {
    vec3 d = p - posRadius.xyz;
    float r2 = posRadius.w * posRadius.w;
    float a = max(0.0, 1.0 - dot(d, d) / max(r2, 0.001));
    return colorIntensity.rgb * (colorIntensity.a * a * a);
}

vec3 dynamicLighting(vec3 p) {
    vec3 l = vec3(0.0);
    if (uDynLightCount > 0) l += pointLight(p, uDynLightPosRadius0, uDynLightColor0);
    if (uDynLightCount > 1) l += pointLight(p, uDynLightPosRadius1, uDynLightColor1);
    if (uDynLightCount > 2) l += pointLight(p, uDynLightPosRadius2, uDynLightColor2);
    if (uDynLightCount > 3) l += pointLight(p, uDynLightPosRadius3, uDynLightColor3);
    return min(l, vec3(2.0));
}

void main() {
    vec2 uv = vUV;
    if (vLiquid > 0.5 && uWaveAmp > 0.0)
        uv += vec2(uTime * 0.02, uTime * 0.013);   // slow surface drift
    vec4 tex = texture(uAtlas, vec3(uv, vLayer));
    if (tex.a < uAlphaCutoff) discard;
    vec3 color = tex.rgb * vShade;
    if (vLiquid > 0.5 && uWaveAmp > 0.0) {
        // Cheap grazing-angle darkening (fresnel stand-in) for water depth cues.
        vec3 toCam = normalize(uCamPos - vWorld);
        float grazing = 1.0 - clamp(abs(toCam.y), 0.0, 1.0);
        color *= 1.0 - 0.30 * grazing * grazing;
    }

    if (uDynLightCount > 0) {
        vec3 dyn = dynamicLighting(vWorld);
        color = max(color, tex.rgb * dyn);
        color += tex.rgb * dyn * 0.10;
    }

    float fog;
    if (uFogMode == 1) {
        // Late-rising exp2 fog that thins with altitude (valleys hold mist),
        // with a hard linear ramp at the render edge so chunk pop-in always
        // hides even where the height falloff thinned the exp2 term.
        float hf = exp2(-min(max(vWorld.y - 62.0, 0.0) * uFogHeightFalloff, 1.2));
        float d = max(vDist - uFogEnd * 0.5, 0.0) * uFogDensity * hf;
        fog = 1.0 - exp2(-d * d);
        fog = max(fog, clamp((vDist - uFogEnd * 0.85) / (uFogEnd * 0.15), 0.0, 1.0));
    } else {
        fog = clamp((vDist - uFogStart) / max(uFogEnd - uFogStart, 0.001), 0.0, 1.0);
    }
    color = mix(color, uFogColor, fog);

    FragColor = vec4(color, tex.a);
}
