#include "/lib/config.glsl"

// MAIN FUNCTION ------------------

#if defined THE_END
    #include "/lib/color_utils_end.glsl"
#elif defined NETHER
    #include "/lib/color_utils_nether.glsl"
#endif

/* Uniforms */

uniform float viewWidth;
uniform float viewHeight;
uniform int frameCounter;
uniform sampler2D tex;
uniform int isEyeInWater;
uniform float nightVision;
uniform float rainStrength;
uniform float dayNightMix;
uniform float pixelSizeX;
uniform float pixelSizeY;
uniform sampler2D gaux4;
uniform sampler2D noisetex;
uniform sampler2D rainrippletex;
#if defined GBUFFER_TERRAIN && defined MC_NORMAL_MAP
    uniform sampler2D normals;
#endif
#if defined GBUFFER_TERRAIN && defined MC_SPECULAR_MAP
    uniform sampler2D specular;
#endif
uniform float frameTimeCounter;

#if defined DISTANT_HORIZONS
    uniform float dhNearPlane;
    uniform float far;
#endif

#if defined GBUFFER_ENTITIES
    uniform int entityId;
    uniform vec4 entityColor;
#endif

#ifdef NETHER
    uniform vec3 fogColor;
#endif

#if defined SHADOW_CASTING
    uniform sampler2DShadow shadowtex1;
    #if defined COLORED_SHADOW
        uniform sampler2DShadow shadowtex0;
        uniform sampler2D shadowcolor0;
    #endif
#endif

uniform float blindness;

#if MC_VERSION >= 11900
    uniform float darknessFactor;
    uniform float darknessLightFactor;
#endif

#ifdef MATERIAL_GLOSS
  // Don't remove
#endif

#if defined MATERIAL_GLOSS && !defined NETHER
    uniform int worldTime;
    uniform vec3 moonPosition;
    uniform vec3 sunPosition;
    uniform mat4 gbufferModelViewInverse;
#endif

#if SHADOW_LOCK > 0 && defined SHADOW_CASTING
    uniform vec3 cameraPosition;
    uniform mat4 shadowModelView;
    uniform mat4 shadowProjection;
    uniform vec3 shadowLightPosition;
#endif

#if defined THE_END || (SHADOW_LOCK > 0 && defined SHADOW_CASTING && !defined NETHER)
    uniform mat4 gbufferModelView;
#endif

/* Ins / Outs */

varying vec2 texcoord;
varying vec4 tintColor;
varying float frogAdjust;
varying vec3 directLightColor;
varying vec3 candleColor;
varying float directLightStrength;
varying vec3 omniLight;

#if defined SHADOW_CASTING && SHADOW_LOCK > 0 && !defined NETHER
    varying vec3 vWorldPos;
    varying vec3 vNormal;
    varying vec3 vBias;
#endif

#if defined GBUFFER_TERRAIN || defined GBUFFER_HAND || defined GBUFFER_ENTITIES
    varying float isEmissiveEntity;
#endif

#ifdef FOLIAGE_V
    varying float isFoliage;
#endif

#if defined SHADOW_CASTING && !defined NETHER
    varying vec3 shadowPos;
    varying float shadowDiffuse;
#endif

#if defined MATERIAL_GLOSS && !defined NETHER
    varying vec3 flatNormal;
    varying vec3 viewPositionRaw;
    varying vec2 lmcoordAlt;
    varying float glossFactor;
    varying float glossPower;
    varying float lumaFactor;
    varying float lumaPower;
    varying vec3 rainWorldPos;
    #if defined GBUFFER_TERRAIN && defined MC_NORMAL_MAP
        varying vec3 prismaTangent;
        varying vec3 prismaBinormal;
    #endif
#endif

/* Utility functions */

#if (defined SHADOW_CASTING && !defined NETHER) || defined DISTANT_HORIZONS
    #include "/lib/dither.glsl"
#endif

#if defined SHADOW_CASTING && !defined NETHER
    #include "/lib/shadow_frag.glsl"
#endif

#include "/lib/luma.glsl"

float prismaSurfaceRainWetness(vec2 position) {
    position -= frameTimeCounter * vec2(0.01, 0.006);
    position *= 0.01;
    float n = texture2D(noisetex, fract(position)).b;
    n += texture2D(noisetex, fract(position * 0.6)).r * 2.0;
    n += texture2D(noisetex, fract(position * 0.2)).g * 3.0;
    return clamp(n * 0.24, 0.0, 1.0) * rainStrength;
}

float prismaHash12(vec2 p) {
    vec3 p3 = fract(vec3(p.xyx) * 0.1031);
    p3 += dot(p3, p3.yzx + 33.33);
    return fract((p3.x + p3.y) * p3.z);
}

vec3 prismaDecodeNormalTex(vec3 normalTex) {
    normalTex = normalTex * 2.0 - 1.0;
    float xyLen = dot(normalTex.xy, normalTex.xy);
    normalTex.z = xyLen < 1.0 ? sqrt(1.0 - xyLen) : 0.0;
    return normalize(normalTex);
}

vec2 prismaSurfaceRainNormal(vec3 worldPos) {
    vec2 position = worldPos.xz;
    position *= 0.6;
    position.x = (position.x + floor(fract(frameTimeCounter) * 64.0)) * 0.015625;

    vec2 normal = texture2D(rainrippletex, fract(position)).rg * 2.0 - 1.0;
    float lod = dot(abs(fwidth(worldPos)), vec3(5.0));
    normal /= (1.0 + lod);

    return normal * 0.55;
}

vec2 prismaSurfaceRainRippleNormal(vec2 worldXZ, float visibility) {
    vec2 grid = worldXZ * 0.42;
    vec2 cellBase = floor(grid);
    vec2 ripple = vec2(0.0);
    float time = frameTimeCounter * 0.72;

    for (int y = -1; y <= 1; y++) {
        for (int x = -1; x <= 1; x++) {
            vec2 cell = cellBase + vec2(float(x), float(y));
            vec2 rand = vec2(prismaHash12(cell + 11.17), prismaHash12(cell + 37.91));
            vec2 center = cell + rand;
            float age = fract(time + prismaHash12(cell + 5.73));
            vec2 d = grid - center;
            float dist = length(d) + 1e-4;
            float radius = mix(0.04, 1.18, age);
            float ring = exp(-24.0 * abs(dist - radius));
            float fade = (1.0 - age) * smoothstep(1.35, 0.08, dist);
            ripple += (d / dist) * ring * fade;
        }
    }

    return ripple * rainStrength * visibility * 0.18;
}

float prismaSurfaceRainRings(vec2 worldXZ, float visibility) {
    vec2 ripple = prismaSurfaceRainRippleNormal(worldXZ, visibility);
    return clamp(length(ripple) * 3.2, 0.0, 1.0);
}




#if defined MATERIAL_GLOSS && !defined NETHER
    #include "/lib/material_gloss_fragment.glsl"
#endif

#if defined SHADOW_CASTING && SHADOW_LOCK > 0 && !defined NETHER
    #include "/lib/shadow_vertex.glsl"
#endif

void main() {
    #if (defined SHADOW_CASTING && !defined NETHER) || defined DISTANT_HORIZONS
        #if AA_TYPE > 0
            float dither = shiftedDither13(gl_FragCoord.xy);
        #else
            float dither = dither17(gl_FragCoord.xy);
        #endif
    #endif
    // Avoid render in DH transition
    #if defined DISTANT_HORIZONS && !defined GBUFFER_BEACONBEAM
        float t = far - dhNearPlane;
        float sup = t * TRANSITION_DH_SUP;
        float inf = t * TRANSITION_DH_INF;
        float umbral = (gl_FogFragCoord - (dhNearPlane + inf)) / (far - sup - inf - dhNearPlane);
        if(umbral > dither) {
            discard;
            return;
        }
    #endif

    // Toma el color puro del bloque
    #if defined GBUFFER_ENTITIES && BLACK_ENTITY_FIX == 1
        vec4 blockColor = texture2D(tex, texcoord);
        if(blockColor.a < 0.1 && entityId != 10101) {   // Black entities bug workaround
            discard;
        }
        blockColor *= tintColor;
    #else
        vec4 blockColor = texture2D(tex, texcoord) * tintColor;
    #endif

        float block_luma = luma(blockColor.rgb);

        vec3 finalCandleColor = candleColor;
    #if defined GBUFFER_TERRAIN || defined GBUFFER_HAND || defined GBUFFER_ENTITIES
        if(isEmissiveEntity > 0.5) {
            finalCandleColor *= block_luma * 1.5;
        }
    #endif

    #ifdef GBUFFER_WEATHER
        float prismaRainTex = clamp(max(blockColor.a, block_luma), 0.0, 1.0);
        vec2 prismaRainUV = vec2(texcoord.x * 1.8 + frameTimeCounter * 0.035, texcoord.y * 10.0 - frameTimeCounter * 0.95);
        float prismaRainNoise = texture2D(noisetex, fract(prismaRainUV)).b;
        float prismaRainBody = smoothstep(0.08, 0.90, prismaRainTex);
        float prismaRainStreak = smoothstep(0.18, 0.95, prismaRainBody * mix(0.72, 1.28, prismaRainNoise));
        vec3 prismaRainTint = mix(vec3(0.48, 0.54, 0.62), vec3(0.76, 0.84, 0.94), clamp(luma(directLightColor) * 0.7 + 0.15, 0.0, 1.0));
        prismaRainTint = mix(prismaRainTint, vec3(0.62, 0.70, 0.80), rainStrength * 0.30);
        blockColor.rgb = prismaRainTint * prismaRainStreak * (0.36 + 0.64 * prismaRainBody);
        blockColor.a = prismaRainStreak * (0.16 + 0.18 * rainStrength);
    #endif

    #if defined GBUFFER_ENTITIES
        // Thunderbolt render
        if(entityId == 10101) {
            blockColor.a = 1.0;
        }
    #endif

    #if defined SHADOW_CASTING && !defined NETHER
        #if SHADOW_LOCK > 0
            vec3 offsetVector = vNormal * 0.002;
            vec3 preSnapPos = vWorldPos + offsetVector;
            float texelSize = SHADOW_LOCK;
            vec3 absPos = preSnapPos + cameraPosition;
            // Redondeo al bloque
            vec3 snappedAbsolute = floor(absPos * texelSize) / texelSize;
            snappedAbsolute += 0.5 / texelSize; // Centrar en el texel
            vec3 final_world_pos = (snappedAbsolute - cameraPosition) + vBias;
            vec3 shadow_real_pos = get_shadow_pos(final_world_pos);
        #else
            vec3 shadow_real_pos = shadowPos;
        #endif

        #if defined COLORED_SHADOW
            vec3 shadowValue = get_colored_shadow(shadow_real_pos, dither);
            shadowValue = mix(shadowValue, vec3(1.0), shadowDiffuse);
        #else
            float shadowValue = get_shadow(shadow_real_pos, dither);
            shadowValue = mix(shadowValue, 1.0, shadowDiffuse);
        #endif
    #else
        float shadowValue = abs((dayNightMix * 2.0) - 1.0);
    #endif

    #if defined COLORED_SHADOW
        float prismaShadowScalar = dot(shadowValue, vec3(0.33333334));
    #else
        float prismaShadowScalar = shadowValue;
    #endif

    #if defined GBUFFER_BEACONBEAM
        blockColor.rgb *= 1.5;
    #elif defined GBUFFER_ENTITY_GLOW
        blockColor.rgb =
            clamp(vec3(luma(blockColor.rgb)) * vec3(0.75, 0.75, 1.5), vec3(0.3), vec3(1.0));
        vec3 realLight = omniLight +
                (shadowValue * directLightColor * directLightStrength) * (1.0 - (rainStrength * 0.75)) +
                finalCandleColor;
    #else
        #if defined MATERIAL_GLOSS && !defined NETHER
            float final_gloss_power = glossPower;
            block_luma *= lumaFactor;

            if(lumaPower < 0.0) {  // Metallic
                final_gloss_power -= (block_luma * 73.334);
            } else {
                block_luma = pow(block_luma, lumaPower);
            }

            float prismaWetFactor = 0.0;
            float prismaSkyExposure = 0.0;
            float prismaRipple = 0.0;
            float prismaTopFacing = 0.0;
            float prismaBaseGlossFactor = glossFactor;
            vec3 prismaBaseNormal = flatNormal;
            vec3 prismaGlossNormal = flatNormal;
            #if defined GBUFFER_TERRAIN
                #ifdef MC_SPECULAR_MAP
                    vec4 prismaSpecTex = texture2D(specular, texcoord);
                    float prismaPbrSmooth = clamp(max(prismaSpecTex.r, prismaSpecTex.g), 0.0, 1.0);
                    float prismaPbrF0 = clamp(prismaSpecTex.g, 0.0, 1.0);
                    prismaBaseGlossFactor = mix(prismaBaseGlossFactor, 0.60 + prismaPbrSmooth * 1.25, 0.80);
                    final_gloss_power = mix(final_gloss_power, 10.0 + prismaPbrSmooth * 90.0, 0.90);
                    block_luma = mix(block_luma, max(block_luma, 0.28 + prismaPbrF0 * 0.72), prismaPbrSmooth * 0.32);
                #endif
                #ifdef MC_NORMAL_MAP
                    vec3 prismaTexNormal = prismaDecodeNormalTex(texture2D(normals, texcoord).rgb);
                    mat3 prismaTBN = mat3(prismaTangent, prismaBinormal, flatNormal);
                    prismaBaseNormal = normalize(mix(flatNormal, prismaTBN * prismaTexNormal, 0.78));
                #endif
                vec3 prismaWorldFlatNormal = normalize(mat3(gbufferModelViewInverse) * flatNormal);
                prismaTopFacing = clamp((prismaWorldFlatNormal.y - 0.5) * 2.5, 0.0, 1.0);
                prismaSkyExposure = clamp(lmcoordAlt.y * 10.0 - 9.0, 0.0, 1.0);
                float prismaWetNoise = prismaSurfaceRainWetness(rainWorldPos.xz - rainWorldPos.y);
                prismaWetNoise *= prismaTopFacing * prismaSkyExposure * ((isEyeInWater == 0) ? 1.0 : 0.0);

                prismaWetNoise *= 1.50;
                float prismaWetMask = smoothstep(0.34, 0.60, prismaWetNoise);
                float prismaWetFilm = smoothstep(0.22, 0.52, prismaWetNoise);
                prismaWetFilm *= prismaWetFilm;

                vec2 prismaRippleNormal = prismaSurfaceRainRippleNormal(rainWorldPos.xz - rainWorldPos.y * 0.16, prismaWetMask * prismaSkyExposure);
                vec2 prismaMicroRipple = prismaSurfaceRainNormal(rainWorldPos) * (prismaWetMask * 0.06);
                prismaRippleNormal += prismaMicroRipple;
                prismaRipple = prismaSurfaceRainRings(rainWorldPos.xz - rainWorldPos.y * 0.16, prismaWetMask * prismaSkyExposure);
                vec3 prismaWetNormal = normalize(vec3(
                    prismaBaseNormal.x + prismaRippleNormal.x * 0.95,
                    prismaBaseNormal.y + prismaWetFilm * 0.10,
                    prismaBaseNormal.z + prismaRippleNormal.y * 0.95
                ));
                prismaGlossNormal = normalize(mix(prismaBaseNormal, prismaWetNormal, clamp(prismaWetMask * 0.88, 0.0, 1.0)));
                prismaWetFactor = clamp(prismaWetFilm * 1.28 + prismaRipple * 0.18, 0.0, 1.0);

                vec3 prismaWetAlbedo = mix(blockColor.rgb, vec3(luma(blockColor.rgb)), 0.20) * vec3(0.80, 0.85, 0.92);
                float prismaRippleShade = prismaRipple * prismaWetMask;
                blockColor.rgb = mix(blockColor.rgb, prismaWetAlbedo, prismaWetFilm * 0.72);
                blockColor.rgb *= 1.0 - prismaRippleShade * 0.018;
                blockColor.rgb += prismaRippleShade * prismaSkyExposure * vec3(0.008, 0.009, 0.011);
            #endif

            vec3 prismaViewPositionNormalized = normalize(viewPositionRaw);
            float prismaDistance = length(viewPositionRaw);
            float prismaDistanceSoften = clamp((prismaDistance - 24.0) / 144.0, 0.0, 1.0);
            float prismaRainGlossPower = mix(final_gloss_power, max(final_gloss_power, 54.0), prismaWetFactor);
            prismaRainGlossPower = mix(prismaRainGlossPower, min(prismaRainGlossPower, 34.0), prismaDistanceSoften * prismaWetFactor * 0.45);
            float prismaDirectionalGloss = mix(1.0, 0.90, prismaWetFactor);
            float material_gloss_factor = materialGloss(reflect(prismaViewPositionNormalized, prismaGlossNormal), lmcoordAlt, prismaRainGlossPower, prismaGlossNormal) * mix(prismaBaseGlossFactor, 1.46, prismaWetFactor) * prismaDirectionalGloss;
            material_gloss_factor *= mix(1.0, 0.92, prismaDistanceSoften * prismaWetFactor);

            float material = material_gloss_factor * mix(block_luma, 0.72 + 0.28 * block_luma, prismaWetFactor);
            float prismaWetSheen = prismaWetFactor * (0.60 + 0.40 * prismaSkyExposure);
            float prismaRippleSparkle = prismaRipple * prismaWetFactor;
            vec3 prismaWetHighlight = (directLightColor * (0.12 + 0.30 * shadowValue) + omniLight * 0.34) * prismaWetSheen * (0.74 + 0.16 * prismaRippleSparkle);
            vec3 prismaRippleHighlight = directLightColor * prismaRippleSparkle * prismaSkyExposure * 0.10;
            vec3 prismaSunHighlight = vec3(0.0);
            #if defined GBUFFER_TERRAIN
                vec3 prismaSunDir = normalize(mix(-sunPosition, sunPosition, dayNightMix));
                float prismaSunGlint = pow(max(dot(reflect(prismaViewPositionNormalized, prismaGlossNormal), prismaSunDir), 0.0), mix(44.0, 62.0, prismaRippleSparkle));
                prismaSunGlint *= prismaWetFactor * prismaSkyExposure * prismaShadowScalar * (0.28 + 0.72 * prismaTopFacing);
                prismaSunGlint *= mix(1.0, 0.88, prismaDistanceSoften);
                prismaSunHighlight = directLightColor * prismaSunGlint * (0.22 + 0.10 * prismaRippleSparkle);
            #endif
            vec3 realLight = omniLight +
                (shadowValue * ((directLightColor * directLightStrength) + (directLightColor * material * 0.56))) * mix(1.0 - (rainStrength * 0.75), 1.0, prismaWetFactor * 0.26) +
                prismaWetHighlight + prismaRippleHighlight + prismaSunHighlight +
                finalCandleColor;
        #else
            vec3 realLight = omniLight +
                (shadowValue * directLightColor * directLightStrength) * (1.0 - (rainStrength * 0.75)) +
                finalCandleColor;
        #endif

#ifdef THE_END
            realLight += vec3(0.10, 0.09, 0.14) * (0.65 + 0.35 * prismaShadowScalar);
        #endif

        blockColor.rgb *= mix(realLight, vec3(1.0), nightVision * 0.125);
        blockColor.rgb *= mix(vec3(1.0, 1.0, 1.0), vec3(NV_COLOR_R, NV_COLOR_G, NV_COLOR_B), nightVision);
    #endif

    #if defined GBUFFER_ENTITIES
        if(entityId == 10101) {
            // Thunderbolt render
            blockColor = vec4(1.0, 1.0, 1.0, 0.5);
        } else {
            float entity_poderation = luma(realLight);  // Red damage bright ponderation
            blockColor.rgb = mix(blockColor.rgb, entityColor.rgb, entityColor.a * entity_poderation * 3.0);
        }
    #endif

    #if MC_VERSION < 11300 && defined GBUFFER_TEXTURED
        blockColor.rgb *= 1.5;
    #endif

    #include "/src/finalcolor.glsl"
    #include "/src/writebuffers.glsl"
}
