#include "/lib/config.glsl"

/* Color utils */

#ifdef THE_END
    #include "/lib/color_utils_end.glsl"
#elif defined NETHER
    #include "/lib/color_utils_nether.glsl"
#else
    #include "/lib/color_utils.glsl"
#endif

/* Uniforms */

uniform sampler2D tex;
uniform float near;
uniform float far;
uniform sampler2D gaux1;
uniform sampler2D gaux4;
uniform mat4 gbufferProjectionInverse;
uniform mat4 gbufferProjection;
uniform sampler2D noisetex;
uniform sampler2D depthtex0;
uniform sampler2D dhDepthTex1;
uniform int isEyeInWater;
uniform vec3 sunPosition;
uniform vec3 moonPosition;
uniform float nightVision;
uniform float rainStrength;
uniform float wetness;
uniform float light_mix;
uniform ivec2 eyeBrightnessSmooth;
uniform float dhNearPlane;
uniform float dhFarPlane;
uniform vec3 cameraPosition;
uniform int dhRenderDistance;
uniform vec4 lightningBoltPosition;

uniform mat4 gbufferModelViewInverse;

#if V_CLOUDS > 0
    uniform sampler2D gaux2;
#endif

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

uniform float blindness;

/* Ins / Outs */

varying vec2 texcoord;
varying vec4 tint_color;
varying vec3 direct_light_color;
varying vec3 candle_color;
varying float direct_light_strength;
varying vec3 omni_light;
varying vec4 position;
varying vec3 fragposition;
varying vec3 tangent;
varying vec3 binormal;
varying vec3 water_normal;
varying vec3 up_vec;
varying float visible_sky;
varying vec2 lmcoord;
varying float block_type;
varying float fog_adj;
varying vec3 hi_sky_color;
varying vec3 mid_sky_color;
varying vec3 low_sky_color;
varying vec3 pure_hi_sky_color;
varying vec3 pure_mid_sky_color;
varying vec3 pure_low_sky_color;

vec4 fragpos = gbufferProjectionInverse * (vec4(gl_FragCoord.xy * vec2(pixel_size_x, pixel_size_y), gl_FragCoord.z, 1.0) * 2.0 - 1.0);
vec3 nfragpos = normalize(fragpos.xyz);

/* Utility functions */

#include "/lib/projection_utils.glsl"
#include "/lib/basic_utils.glsl"
#include "/lib/dither.glsl"
#include "/lib/astral_living_water.glsl"
#include "/lib/water_dh.glsl"
#include "/lib/depth.glsl"
#include "/lib/luma.glsl"
#include "/src/current_sky_color.glsl"

#if !defined NETHER && !defined THE_END
    #include "/lib/cosmic_rift.glsl"
    #include "/lib/daylight_halo.glsl"
    #include "/lib/colossus_planet.glsl"
#endif

#define FRAGMENT
#include "/lib/downscale.glsl"

void main() {
    if(fragment_cull()) discard;
    vec2 eye_bright_smooth = vec2(eyeBrightnessSmooth);
    vec3 real_light;

    #if AA_TYPE > 0 
        float dither = shifted_r_dither(gl_FragCoord.xy);
    #else
        float dither = r_dither(gl_FragCoord.xy);
        // dither = 1.0;
    #endif
    
    // Avoid render unnecessary DH
    float t = far - dhNearPlane;
    float inf = t * TRANSITION_DH_INF;
    float view_dist = length(position.xyz);
    float d = texture2DLod(depthtex0, vec2(gl_FragCoord.x / viewWidth, gl_FragCoord.y / viewHeight), 0.0).r;
    float linear_d = ld(d);

    if(linear_d < 0.9999 || view_dist < dhNearPlane + inf) {
        discard;
        return;
    }

    #ifdef VANILLA_WATER
        vec3 water_normal_base = vec3(0.0, 0.0, 1.0);
    #else
        vec3 world_pos = position.xyz + cameraPosition;
        vec3 water_normal_base = normal_waves_dh(world_pos.xzy);
    #endif

    vec3 surface_normal;
    if(block_type < DH_BLOCK_WATER + 0.5 && block_type > DH_BLOCK_WATER - 0.5) {  // Water
        surface_normal = get_normals(water_normal_base, fragposition);
    } else {
        surface_normal = get_normals(vec3(0.0, 0.0, 1.0), fragposition);
    }

    float normal_dot_eye = dot(surface_normal, normalize(fragposition));
    float fresnel = square_pow(1.0 + normal_dot_eye);

    vec3 reflect_water_vec = reflect(fragposition, surface_normal);
    vec3 norm_reflect_water_vec = normalize(reflect_water_vec);

    vec3 sky_color_reflect;
    if(isEyeInWater == 0 || isEyeInWater == 2) {
        sky_color_reflect = mix(current_low_sky_color, hi_sky_color, smoothstep(0.0, 1.0, pow(clamp(dot(norm_reflect_water_vec, up_vec), 0.0001, 1.0), 0.333)));
    } else {
        sky_color_reflect = hi_sky_color * .5 * ((eye_bright_smooth.y * .8 + 48) * 0.004166666666666667);
    }

    sky_color_reflect = xyz_to_rgb(sky_color_reflect);

    float cosmic_rift_reflect_mask = 0.0;

    #if !defined NETHER && !defined THE_END
        vec3 worldReflectDir = normalize(
            (gbufferModelViewInverse * vec4(reflect_water_vec, 0.0)).xyz);
        #ifdef ASTRAL_DAY_HALO
            vec3 haloSunWorldDir = normalize(
                (gbufferModelViewInverse
                    * vec4(normalize(sunPosition), 0.0)).xyz);
            vec4 daylightHaloReflection = getAstralDayHalo(
                worldReflectDir, haloSunWorldDir,
                sky_color_reflect);
            sky_color_reflect = sky_color_reflect
                * (1.0 - daylightHaloReflection.a)
                + daylightHaloReflection.rgb;
        #endif

        #ifdef ASTRAL_COLOSSUS_PLANET
            vec3 colossusSunWorldDir = normalize(
                (gbufferModelViewInverse
                    * vec4(normalize(sunPosition), 0.0)).xyz);
            vec4 colossusReflection = getAstralColossus(
                worldReflectDir, colossusSunWorldDir,
                sky_color_reflect);
            sky_color_reflect = sky_color_reflect
                * (1.0 - colossusReflection.a)
                + colossusReflection.rgb;
        #endif

        #if defined COSMIC_RIFT && defined COSMIC_RIFT_REFLECTIONS
            vec3 stableRiftNormal = normalize(mix(
                surface_normal, normalize(water_normal), 0.58));
            vec3 stableRiftViewDir = reflect(
                normalize(fragposition), stableRiftNormal);
            vec3 worldRiftReflectDir = normalize(
                (gbufferModelViewInverse
                    * vec4(stableRiftViewDir, 0.0)).xyz);

            if (worldRiftReflectDir.y > 0.0) {
                vec4 cosmicRiftWash = getCosmicSkyWash(
                    worldRiftReflectDir, sky_color_reflect);
                vec4 cosmicRiftReflect = getCosmicRift(
                    worldRiftReflectDir, sky_color_reflect);
                vec3 cataclysmFrontReflect =
                    getCosmicCataclysmFront(worldRiftReflectDir);
                sky_color_reflect = sky_color_reflect
                    * (1.0 - cosmicRiftReflect.a)
                    + cosmicRiftReflect.rgb
                    + cataclysmFrontReflect;

                float localRiftCoverage = max(
                    cosmicRiftReflect.a - cosmicRiftWash.a, 0.0)
                    / max(1.0 - cosmicRiftWash.a, 0.05);
                cosmic_rift_reflect_mask = max(
                    clamp(localRiftCoverage * 2.15, 0.0, 1.0),
                    smoothstep(0.004, 0.075,
                        luma(cataclysmFrontReflect)));
            }
        #endif
    #endif

    #if !defined VANILLA_WATER && WATER_TEXTURE == 1
        vec4 block_color = vec4(0.1);
        // Synthetic water texture
        vec3 synth_pos = (position.xyz + cameraPosition) * 8.0;
        synth_pos = floor(synth_pos + 0.01);
        float noise = hash13(synth_pos);
        noise *= noise;
        noise *= noise;
        noise *= noise;
        float synth_noise = (noise * 0.3) + 0.5;
        block_color.rgb += vec3(synth_noise);
    #elif defined VANILLA_WATER
        // Synthetic water texture
        vec3 synth_pos = (position.xyz + cameraPosition) * 8.0;
        synth_pos = floor(synth_pos + 0.01);
        float noise = hash13(synth_pos);
        noise *= noise;
        noise *= noise;
        float synth_noise = (noise * 0.227) + 0.773;
        vec4 block_color = vec4(vec3(synth_noise), tint_color.a);
    #else
        vec4 block_color;
    #endif

    if(block_type < DH_BLOCK_WATER + 0.5 && block_type > DH_BLOCK_WATER - 0.5) {  // Water
    #ifdef VANILLA_WATER
        float shadow_c = abs((light_mix * 2.0) - 1.0);

        float fresnel_tex = luma(block_color.rgb);

        real_light = omni_light +
            (direct_light_strength * shadow_c * direct_light_color) * (1.0 - rainStrength * 0.75) +
            candle_color;

        real_light *= (fresnel_tex * 2.0) - 0.25;

        block_color.rgb *= mix(real_light, vec3(1.0), nightVision * .125) * tint_color.rgb;

        block_color.rgb = water_shader_dh(fragposition, surface_normal, block_color.rgb, sky_color_reflect, norm_reflect_water_vec, fresnel, visible_sky, dither, direct_light_color, cosmic_rift_reflect_mask);

        block_color.a = sqrt(block_color.a);
    #else
        #if WATER_TEXTURE == 1
            float water_texture = luma(block_color.rgb);
        #else
            float water_texture = 1.0;
        #endif

            real_light = omni_light +
                (direct_light_strength * visible_sky * direct_light_color) * (1.0 - rainStrength * 0.75) +
                candle_color;

            // === AURORA WATER COLOR SYSTEM (DH) ===
            float water_dist_approx = clamp(view_dist * 0.005, 0.0, 1.0);

            vec3 astral_water_color;
            vec3 living_crest_color;
            float living_water_pattern;
            float living_water_energy;
            float living_night_amount;
            getAstralLivingWater(
                world_pos.xz, water_dist_approx,
                astral_water_color, living_crest_color,
                living_water_pattern, living_water_energy,
                living_night_amount);
            water_texture *= living_water_pattern;
            vec3 living_night_fill = mix(
                vec3(0.0), vec3(0.030, 0.050, 0.115),
                living_night_amount) * visible_sky;

        #if WATER_COLOR_SOURCE == 0
                block_color.rgb = water_texture
                    * (real_light + living_night_fill)
                    * astral_water_color;
        #elif WATER_COLOR_SOURCE == 1
            block_color.rgb = 0.3 * water_texture * real_light * tint_color.rgb;
        #endif

            block_color = vec4(refraction(fragposition, block_color.rgb, water_normal_base), 1.0);

            block_color.rgb += living_crest_color
                * living_water_energy
                * mix(0.0, 0.030, living_night_amount)
                * visible_sky * (1.0 - rainStrength * 0.52);

            // Subsurface scattering (simplified for DH)
            float normal_dot_eye_dh = dot(surface_normal, normalize(fragposition));
            float sss_factor = pow(max(1.0 + normal_dot_eye_dh, 0.0), 3.0) * 0.06;
            vec3 sss_color = living_crest_color;
            block_color.rgb += sss_color * sss_factor * real_light * visible_sky;

        #if WATER_TEXTURE == 1
            fresnel = clamp(fresnel * (water_texture * water_texture + 0.5), 0.0, 1.0);
        #endif

            fresnel = mix(fresnel, pow(fresnel, 0.7), 0.4);

        block_color.rgb = water_shader_dh(fragposition, surface_normal, block_color.rgb, sky_color_reflect * day_blend_float(1.15, 1.15, 1.0), norm_reflect_water_vec, fresnel, visible_sky, dither, direct_light_color, cosmic_rift_reflect_mask);
        block_color.rgb = applyAstralLivingWaterSurface(
            block_color.rgb, astral_water_color,
            living_crest_color, living_water_pattern,
            living_water_energy, living_night_amount,
            visible_sky, rainStrength, view_dist);
    #endif

    } else {  // Otros translúcidos

        block_color = tint_color;

        float shadow_c = abs((light_mix * 2.0) - 1.0);

        real_light = omni_light +
            (direct_light_strength * shadow_c * direct_light_color) * (1.0 - rainStrength * 0.75) +
            candle_color;

        block_color.rgb *= mix(real_light, vec3(1.0), nightVision * .125);
    }

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