#version 130
#include "commonSettings.glsl"

#define VORONOI_ENTITY_DISTORTION 1 // Enable voronoi entity distortion [0 1]

uniform sampler2D noisetex;
uniform sampler2D texture;
uniform sampler2D lightmap;

uniform mat4 gbufferModelViewInverse;
uniform mat4 gbufferModelView;
uniform mat4 shadowModelView;
uniform mat4 shadowProjection;
uniform vec4 entityColor;
uniform vec3 shadowLightPosition;
uniform vec3 skyColor;
uniform ivec2 atlasSize;
uniform float blindness;
uniform int isEyeInWater;
uniform sampler2D shadowcolor0;
uniform sampler2D shadowtex0;
uniform sampler2D shadowtex1;
uniform float rainStrength;
uniform vec3 upPosition;
uniform vec3 fogColor;
uniform float far;
uniform float sunAngle;
uniform ivec2 eyeBrightnessSmooth;
uniform int heldItemId;
uniform int heldBlockLightValue;
uniform vec3 eyePosition; // player eye in view space — always (0,0,0)

varying vec4 color;
varying vec3 shadowPos;
varying vec3 world;
varying vec3 vert;
varying vec2 coord0;
varying vec2 coord1;
varying float id;
varying vec3 vPos;
varying vec4 N;

vec2 hash2(vec2 p)
{
    return fract(cos(p*mat2(85.6,-69.3,74.8,-81.2))*475.);
}

vec2 voronoi(vec2 p,vec2 s)
{
    float d = 2.;
    vec2 off = vec2(.5);
    vec2 f = floor(p);

    for(float i = -1.;i<=1.;i++)
    for(float j = -1.;j<=1.;j++)
    {
        vec2 o = (hash2(f+s+vec2(i,j))-.5)+f+.5+vec2(i,j)-p;
        float t = length(o);
        if (d>t)
        {
            d = t;
            off = f+.5+vec2(i,j);
        }
    }
    return off;
}
vec2 off(vec2 p,vec3 s)
{
    return voronoi(p,s.xy+s.yz);
}

// Shadow sampling array https://github.com/jhk2/glsandbox/blob/master/kgl/samples/shadow/pcss.glsl
const vec2 offsets[64] = vec2[64](
    vec2(-0.04117257, -0.1597612),
    vec2(0.06731031, -0.4353096),
    vec2(-0.206701, -0.4089882),
    vec2(0.1857469, -0.2327659),
    vec2(-0.2757695, -0.159873),
    vec2(-0.2301117, 0.1232693),
    vec2(0.05028719, 0.1034883),
    vec2(0.236303, 0.03379251),
    vec2(0.1467563, 0.364028),
    vec2(0.516759, 0.2052845),
    vec2(0.2962668, 0.2430771),
    vec2(0.3650614, -0.1689287),
    vec2(0.5764466, -0.07092822),
    vec2(-0.5563748, -0.4662297),
    vec2(-0.3765517, -0.5552908),
    vec2(-0.4642121, -0.157941),
    vec2(-0.2322291, -0.7013807),
    vec2(-0.05415121, -0.6379291),
    vec2(-0.7140947, -0.6341782),
    vec2(-0.4819134, -0.7250231),
    vec2(-0.7627537, -0.3445934),
    vec2(-0.7032605, -0.13733),
    vec2(0.8593938, 0.3171682),
    vec2(0.5223953, 0.5575764),
    vec2(0.7710021, 0.1543127),
    vec2(0.6919019, 0.4536686),
    vec2(0.3192437, 0.4512939),
    vec2(0.1861187, 0.595188),
    vec2(0.6516209, -0.3997115),
    vec2(0.8065675, -0.1330092),
    vec2(0.3163648, 0.7357415),
    vec2(0.5485036, 0.8288581),
    vec2(-0.2023022, -0.9551743),
    vec2(0.165668, -0.6428169),
    vec2(0.2866438, -0.5012833),
    vec2(-0.5582264, 0.2904861),
    vec2(-0.2522391, 0.401359),
    vec2(-0.428396, 0.1072979),
    vec2(-0.06261792, 0.3012581),
    vec2(0.08908027, -0.8632499),
    vec2(0.9636437, 0.05915006),
    vec2(0.8639213, -0.309005),
    vec2(-0.03422072, 0.6843638),
    vec2(-0.3734946, -0.8823979),
    vec2(-0.3939881, 0.6955767),
    vec2(-0.4499089, 0.4563405),
    vec2(0.07500362, 0.9114207),
    vec2(-0.9658601, -0.1423837),
    vec2(-0.7199838, 0.4981934),
    vec2(-0.8982374, 0.2422346),
    vec2(-0.8048639, 0.01885651),
    vec2(-0.8975322, 0.4377489),
    vec2(-0.7135055, 0.1895568),
    vec2(0.4507209, -0.3764598),
    vec2(-0.395958, -0.3309633),
    vec2(-0.6084799, 0.02532744),
    vec2(-0.2037191, 0.5817568),
    vec2(0.4493394, -0.6441184),
    vec2(0.3147424, -0.7852007),
    vec2(-0.5738106, 0.6372389),
    vec2(0.5161195, -0.8321754),
    vec2(0.6553722, -0.6201068),
    vec2(-0.2554315, 0.8326268),
    vec2(-0.5080366, 0.8539945)
);

#define rot2D(r) mat2(cos(r), sin(r), -sin(r), cos(r))

// PCF shadow sampling
float sPCF(sampler2D sTex, vec3 sSPos, float bRad){
    float sMap = 0.0;
    float bNoise = texture(noisetex, gl_FragCoord.xy / vec2(noiseTextureResolution)).r;
    for(int i = 0; i < PCF_SAMPLE; i++){
        sMap += step(sSPos.z, texture(sTex, sSPos.xy + (rot2D(bNoise * 6.28318531) * offsets[i] * bRad)).r);
    }
    return sMap / PCF_SAMPLE;
}

#if HANDHELD_LIGHT == 1
vec3 getHandheldLight(vec3 fragViewPos, int lightVal) {
    // lightVal is 0-15, normalize to 0-1
    float strength = float(lightVal) / 15.0;
    if (strength <= 0.0) return vec3(0.0);

    // Distance from the camera (eye) to the fragment in view space
    float dist = length(fragViewPos);

    // Smooth quadratic falloff clamped to range
    float attenuation = max(0.0, 1.0 - dist / (HANDHELD_LIGHT_RANGE * strength));
    attenuation = attenuation * attenuation; // quadratic for natural falloff

    // Warm torch-like tint — tweak RGB to taste
    vec3 lightColor = vec3(1.0, 0.75, 0.4);

    return lightColor * attenuation * HANDHELD_LIGHT_STRENGTH * strength;
}
#endif

/* RENDERTARGETS:0,4 */
void main()
{
    vec3 dir = normalize((gbufferModelViewInverse * vec4(shadowLightPosition,0)).xyz);
    float flip = clamp(dir.y/.1,-1.,1.); dir *= flip;
    vec3 norm = normalize(cross(dFdx(world),dFdy(world)));
    float lambert = dot(norm,dir)*.5+.5;

    // Cave detector - computed early so it can be used for fog too
    float skyLightFactor = clamp(eyeBrightnessSmooth.y / 240.0, 0.0, 1.0);

    //fog calculation
    float zSky = clamp(dot(normalize(vPos), normalize(upPosition)), 0.0, 1.0);
    vec3 skyFog = mix(fogColor, skyColor, zSky);
    skyFog = mix(vec3(0.0), skyFog, skyLightFactor);
    
    //SHADOW CALCULATION
    vec3 sLight = vec3(1.0);
    #if ENABLE_SHADOWS == 1
        if(length(world) < shadowDistance && id != 1.0){
            // Transform world position to shadow space
            vec3 sWorld = vec3(shadowModelView * vec4(world, 1.0));
            vec3 sClip = vec3(shadowProjection * vec4(sWorld, 1.0));

            // Apply distortion to shadow coordinates
            sClip.xyz = distortShadow(sClip.xyz);

            //bias calculation matching gbuffers_textured
            float sBias = 0.0;
            switch(shadowMapResolution){
                case 2048: sBias = 0.0039296875; break;
                case 4096: sBias = 0.002953125; break;
                case 8192: sBias = 0.002220703125; break;
                default: sBias = 0.00480625;
            }

            float dist = mix(1.0, length(sClip.xy), 0.85);
            sClip.xy /= dist;
            sClip.z -= (dist * dist * sBias) / clamp(dot(normalize(shadowLightPosition), normalize(upPosition)), 0.0, 1.0);
            
            // Normal-based bias
            float ndotl = max(dot(normalize(N.xyz), normalize(shadowLightPosition)), 0.001);
            float finalBias = sBias * (1.0 / ndotl) * (dist * 0.5);
            sClip.z -= finalBias;
            
            vec3 sSPos = sClip.xyz * 0.5 + 0.5;

            // Shadow sampling
            #if SOFT_SHADOWS == 1
                float bRad = PCF_BLUR_RADIUS / shadowMapResolution;
                float sMap0 = sPCF(shadowtex0, sSPos, bRad);
                float sMap1 = sPCF(shadowtex1, sSPos, bRad);
            #else
                float sMap0 = step(sSPos.z, texture(shadowtex0, sSPos.xy).r);
                float sMap1 = step(sSPos.z, texture(shadowtex1, sSPos.xy).r);
            #endif

            // Colored shadows
            #if COLORED_SHADOWS == 1
                vec4 sMapC = texture2D(shadowcolor0, sSPos.xy);
                sLight = mix(sLight, sMapC.rgb, sqrt(sMapC.a * 2.0)) * (sMap1 - sMap0);
                sLight += sMap0;
            #else
                sLight = vec3(sMap1 + sMap0) * 0.5;
            #endif

            // Directional lighting for non-water entities
            if(N.a < 1.0){
                sLight = sLight * clamp(dot(normalize(shadowLightPosition), N.xyz), 0.0, 1.0) * 2.0;
            }
        }
    #endif

	// Fade sLight during twilight shadow-map transition (same logic as gbuffers_textured)
	float sunlightFade;
	{
		float sunsetFade = 1.0 - smoothstep(0.49, 0.50, sunAngle) * (1.0 - smoothstep(0.507, 0.511, sunAngle));
		sunsetFade = clamp(sunsetFade, 0.0, 1.0);
		float wrappedAngle = sunAngle > 0.5 ? sunAngle - 1.0 : sunAngle;
		float sunriseFade = 1.0 - smoothstep(-0.03, 0.0, wrappedAngle) + smoothstep(0.0, 0.06, wrappedAngle);
		sunriseFade = clamp(sunriseFade, 0.0, 1.0);
		sunlightFade = min(sunsetFade, sunriseFade);
	}
	sLight = mix(vec3(1.0), sLight, sunlightFade);

	// LIGHTING CALCULATION
	float sBright = mix(SHADOW_BRIGHTNESS, 1.0, rainStrength);
	float lVis = texture2D(lightmap, vec2(0.0, coord1.y)).r;

	// Cave detector: eye-position sky light (already declared above for fog use)
	float sBrightForMap = mix(1.0, sBright, skyLightFactor);

	float blockLightCoord = coord1.x * BLOCK_LIGHT_INTENSITY;
	blockLightCoord = clamp(blockLightCoord, 0.0, 1.0); // Keep within valid range

	vec3 lMap = texture2D(lightmap, vec2(blockLightCoord, coord1.y * sBrightForMap)).rgb;

	lMap *= color.a;
	sBright += (1.0 - lVis) * (1.0 - sBright);
	lMap += sLight * (1.0 - sBright) * skyLightFactor;
	lMap *= mix(CAVE_DARKNESS-0.1, 1.0, skyLightFactor);
	
	// === HANDHELD LIGHT ===
    #if HANDHELD_LIGHT == 1
        lMap += getHandheldLight(vPos, heldBlockLightValue);
    #endif

	vec3 effectiveSkyColor = mix(vec3(0.6), skyColor, skyLightFactor);
	#if ENABLE_DIRECTIONAL_LIGHTING == 1
		vec3 directionalShad = mix(effectiveSkyColor*.5+.2, vec3(1), lambert) * lMap;
		vec3 flatShad = vec3(1) * lMap;
		vec3 shad = mix(flatShad, directionalShad, skyLightFactor);
	#else
		vec3 shad = vec3(1) * lMap;
	#endif

    vec2 res = vec2(textureSize(texture,0));
    vec2 cell = coord0*res;
    vec2 floo = floor(cell);
    vec2 mi = floor(floo/16.)*16.;
    vec2 ma = mi+15.;
    vec3 dif = floor(vert);
    
    // Apply voronoi distortion only if enabled
    #if ENABLE_VORONOI_DISTORTION == 1 && VORONOI_ENTITY_DISTORTION == 1
        vec2 shift = off(cell,dif);
        vec2 off1 = clamp(shift,mi,ma)/res;
    #else
        vec2 off1 = coord0;
    #endif

    vec2 gx = dFdx(coord0);
    vec2 gy = dFdy(coord0);
    vec4 col = textureGrad(texture,off1,gx,gy);
    col.a = min(textureGrad(texture,coord0,gx,gy).a,col.a);

    // Store original texture color before lighting
    vec4 originalTexture = col;

    col *= color * vec4(shad*(1.-blindness),1);
    
    //fog application
    float farDist = far;
    float fogDist = clamp(length(world) / farDist, 0.0, 1.0);
        fogDist = (isEyeInWater == 1) ? fogDist : pow(fogDist, 3.0);
    col.rgb = mix(col.rgb, skyFog, fogDist);
    
    col.rgb = mix(col.rgb,entityColor.rgb,entityColor.a);
    
    // Store original texture color with vertex color applied
    vec4 originalTextureColor = originalTexture * vec4(color.rgb, 1.0);
    
    // Output to multiple buffers
    gl_FragData[0] = col; // Final lit color to colortex0
    gl_FragData[1] = vec4(originalTextureColor.rgb, 1.0); // Original color + shadow info to colortex4
}