varying vec2 lmcoord, texcoord;

varying vec3 normalVO, normalWO;

varying vec4 glcolor;

varying float worldDis0;
varying vec4 vViewPos, vWorldPos, vMcPos;
varying float isNoon, isNight, sunRiseSet;
varying float isNoonS, isNightS, sunRiseSetS;

varying vec3 sunWorldDir, moonWorldDir, lightWorldDir;
varying vec3 sunViewDir, moonViewDir, lightViewDir;

varying vec3 sunColor, skyColor;

varying mat3 tbnMatrix;

#include "/lib/uniform.glsl"
#include "/lib/settings.glsl"
#include "/lib/common/utils.glsl"
#include "/lib/camera/colorToolkit.glsl"
#include "/lib/common/position.glsl"
#include "/lib/common/noise.glsl"
#include "/lib/common/normal.glsl"

#include "/lib/atmosphere/celestial.glsl"

#include "/lib/lighting/lightmap.glsl"
#include "/lib/lighting/shadowMapping.glsl"

#include "/lib/water/waterNormal.glsl"
#include "/lib/water/waterFog.glsl"
#include "/lib/water/waterReflectionRefraction.glsl"
#include "/lib/surface/PBR.glsl"

#ifdef FSH
#include "/lib/water/translucentLighting.glsl"

flat in float isWater, isIce;

void main() {
    bool isUnderwater = (isEyeInWater == 1);
    bool isAbovewater = (isEyeInWater == 0);
    
    vec2 texGradX = dFdx(texcoord);
    vec2 texGradY = dFdy(texcoord);
    vec2 parallaxUV = texcoord;
    vec2 fragCoord = gl_FragCoord.xy * invViewSize;

    float depth1O = texture(depthtex1, fragCoord).r;
    vec4 viewpos1O = screenPosToViewPos(vec4(fragCoord, depth1O, 1.0));
    vec3 viewDir = normalize(viewpos1O.xyz);
    vec4 worldPos1O = viewPosToWorldPos(viewpos1O);
    vec3 worldDir = normalize(worldPos1O.xyz);

    vec4 mcPos = vMcPos;

    vec4 texColor = texture(tex, texcoord) * glcolor;
    if(texColor.a < 0.005) discard;
    vec4 color = vec4(BLACK, 1.0);

    vec3 normalTexV = normalize(tbnMatrix * (textureGrad(normals, parallaxUV, texGradX, texGradY).rgb * 2.0 - 1.0));
    vec3 normalTexW = normalize(gbufferModelViewInverse * vec4(normalTexV, 0.0)).xyz;
    vec4 specularMap = texture(specular, texcoord);

    vec2 lightmap = AdjustLightmap(lmcoord);
    
    if(isWater > 0.5){
        vec3 viewDirTS = normalize(vViewPos.xyz * tbnMatrix);
        float parallaxHeight = 1.0;
        vec2 waveParallaxUV = mcPos.xz;
        #ifdef WAVE_PARALLAX
            waveParallaxUV = waveParallaxMapping(mcPos.xz, viewDirTS, parallaxHeight);
        #endif
        vec3 waveViewNormal = normalize(tbnMatrix * getWaveNormal(waveParallaxUV));
        vec3 waveWorldNormal = viewPosToWorldPos(vec4(waveViewNormal, 0.0)).xyz;

        vec2 refractCoord = saturate(waterRefractionCoord(normalVO, waveViewNormal, worldDis0));
        float depth1 = texture(depthtex1, refractCoord).r;
        vec4 viewPos1 = screenPosToViewPos(vec4(refractCoord, depth1, 1.0));
        vec4 worldPos1 = viewPosToWorldPos(viewPos1);

        #if MC_VERSION < 11400
            worldPos1 -= vec4(0.0, 2.0, 0.0, 0.0);
        #endif

        float worldDis1 = length(worldPos1.xyz);
        vec3 worldDir = normalize(worldPos1.xyz);
        vec4 fWorldPos1 = vec4(min(worldDis1, far) * worldDir, 1.0);

        float cosI = dot(-worldDir, waveWorldNormal);
        float sinT2 = WATER_REFRAT_IOR * WATER_REFRAT_IOR * (1.0 - cosI * cosI);
        #ifdef UNDERWATER_REFLECTION
            float TIR = step(1.0, sinT2);
        #else
            float TIR = 0.0;
        #endif

        #ifdef WATER_REFRACTION
            bool useRefract = (worldPos1.y < vWorldPos.y || normalWO.y < 0.5);
            vec2 sampleCoord = useRefract ? refractCoord : fragCoord;
            vec3 colorRGB = textureLod(gaux1, sampleCoord, 1).rgb * 1.25;

            worldPos1 = mix(worldPos1O, worldPos1, float(useRefract));
            worldDis1 = mix(length(worldPos1O.xyz), worldDis1, float(useRefract));

            float TIRFactor = 1.0;
            if(useRefract && isUnderwater) {
                TIRFactor = (1.0 - TIR) * eyeBrightnessSmooth.y / 240.0;
            }
            colorRGB *= TIRFactor;
            color = vec4(colorRGB * COLOR_UI_SCALE, 1.0);
        #endif

        float deep = worldDis1 - worldDis0;
        vec3 fogColor = waterFogColor *
                (mix(vec3(getLuminance(sunColor)), sunColor, 0.5) * 0.125 +
                NIGHT_VISION_BRIGHTNESS * nightVision);

        float lightmapY = saturate(lightmap.y + NIGHT_VISION_BRIGHTNESS * nightVision + 0.015);

        if (isAbovewater) {
            float depthFactor = saturate(deep / WATER_MIST_VISIBILITY);
            vec3 fogAttenuation = saturate(fastExp(-(vec3(1.0) - fogColor) * deep * WATER_FOG_TRANSMIT));
            
            color.rgb *= fogAttenuation;
            vec3 deepBlue = vec3(0.02, 0.20, 0.40);

color.rgb = mix(color.rgb, deepBlue * lightmapY, depthFactor * 0.9);

        }

        vec3 reflectWorldDir = reflect(worldDir, waveWorldNormal);
        vec3 reflectViewDir = reflect(viewDir, waveViewNormal);
    
        float underwaterFactor = isUnderwater ? 0.0 : 1.0;
        bool ssrTargetSampled = false;
        
        float cosTheta = dot(-worldDir, waveWorldNormal);
        float fresnel = mix(pow(1.0 - saturate(cosTheta), REFLECTION_FRESNAL_POWER), 1.0, WATER_F0);
        
        // Boost water reflection during rain
        fresnel = mix(fresnel, 1.0, rainStrength * 0.3);

        #ifdef WATER_REFLECTION
            vec3 reflectColor = reflection(
                colortex8, 
                vViewPos.xyz, 
                reflectWorldDir, 
                reflectViewDir, 
                lightmapY * underwaterFactor, 
                normalVO, 
                COLOR_UI_SCALE, 
                ssrTargetSampled
            );
            
            if (isAbovewater) {
                color.rgb = mix(color.rgb, reflectColor, fresnel);
            } else {
                color.rgb += fogColor * 0.2 * lightmapY * TIR;
                color.rgb = mix(color.rgb, reflectColor, saturate(float(ssrTargetSampled) * TIR));
            }
        #endif

        #ifdef WATER_REFLECT_HIGH_LIGHT
            float shade = 1.0;

            #if MC_VERSION >= 11400
                #ifdef TRANSLUCENT_SHADOW
                    shade = shadowMappingTranslucent(vWorldPos, normalTexW, 0.5, 1.0);
                #endif
            #endif

            MaterialParams params;
            params.roughness = 0.5;
            params.metalness = 0.5;

            vec3 BRDF = reflectPBR(viewDir, waveViewNormal, sunViewDir, params);
            float lightmapMask = remapSaturate(lightmap.y, 0.5, 1.0, 0.0, 1.0) * shade;

            color.rgb += drawCelestial(reflectWorldDir, 1.0, false)
                         * lightmapMask * WATER_REFLECT_HIGH_LIGHT_INTENSITY * 0.5;
        #endif

    } else {
        color.a = texColor.a;
        vec3 albedo = toLinearR(texColor.rgb);
        vec3 diffuse = albedo / PI;

        MaterialParams materialParams = MapMaterialParams(specularMap);

        materialParams.smoothness = TRANSLUCENT_ROUGHNESS;
        float perceptual_roughness = 1.0 - materialParams.smoothness;
        materialParams.roughness = perceptual_roughness * perceptual_roughness;
        materialParams.metalness = TRANSLUCENT_F0;

        #ifdef PBR_REFLECTIVITY
            mat2x3 PBR = CalculatePBR(viewDir, normalTexV, lightViewDir, albedo, materialParams);
            vec3 BRDF = PBR[0] + PBR[1];
            vec3 BRDF_D = vec3(1.0 - materialParams.metalness * 0.9) * diffuse;
        #else
            vec3 BRDF = diffuse;
            vec3 BRDF_D = BRDF;
        #endif

        float cos_theta_O = dot(normalTexW, lightWorldDir);
        float cos_theta = max(cos_theta_O, 0.0);

        float UoN = dot(normalTexW, upWorldDir);
        vec3 skyColorMix = mix(sunColor, skyColor, 0.98 - 0.05 * lightmap.y);
        float hemiWeight = mix(1.0, UoN * 0.5 + 0.5, 0.66);
        vec3 skyLight = lightmap.y * BRDF_D * skyColorMix * hemiWeight;

        float shade = 1.0;

        #if MC_VERSION >= 11400
            #ifdef TRANSLUCENT_SHADOW
                shade = shadowMappingTranslucent(vWorldPos, normalTexW, TRANSLUCENT_SHADOW_SOFTNESS, TRANSLUCENT_SHADOW_QUALITY);
            #endif
        #endif

        #ifdef NETHER
            shade = 0.0;
        #endif

        vec3 direct = sunColor * BRDF * shade * cos_theta;
        
        vec3 artificial = lightmap.x * artificial_color * diffuse;
        artificial += saturate(materialParams.emissiveness) * diffuse * EMISSIVENESS_BRIGHTNESS;

        vec3 c = albedo * 0.005;
        c += skyLight * SKY_LIGHT_BRIGHTNESS * 1.0;
        c += nightVision * diffuse * NIGHT_VISION_BRIGHTNESS;
        c += direct * DIRECT_LUMINANCE * 1.0;
        c += artificial;

        #ifdef PBR_REFLECTIVITY
        {
            vec3 reflectWorldDir = reflect(worldDir, normalTexW);
            vec3 reflectViewDir = reflect(viewDir, normalTexV);
            float NdotU = dot(upWorldDir, reflectWorldDir);
            float upDirFactor = smoothstep(-1.0, 0.0, NdotU);
            bool ssrTargetSampled = false;    
            vec3 reflectColor = reflection(
                colortex8,
                vViewPos.xyz,
                reflectWorldDir,
                reflectViewDir,
                lightmap.y * upDirFactor,
                normalTexV,
                COLOR_UI_SCALE,
                ssrTargetSampled
            ) + drawCelestial(reflectWorldDir, 1.0, false) * shade;

            float NdotV = saturate(dot(normalTexV, -viewDir));

            vec3 F0 = mix(vec3(0.04), albedo, materialParams.metalness); 
            vec3 BRDF = EnvDFGLazarov(F0, materialParams.smoothness, NdotV)
                        * pow(materialParams.smoothness, 1.0 / MIRROR_INTENSITY);

            c += reflectColor * BRDF;
        }
        #endif

        #define TRANSLUCENT_MODE 1
        #if TRANSLUCENT_MODE == 0
            color.rgb = mix(texture(gaux1, fragCoord).rgb * COLOR_UI_SCALE, c, color.a);
            color.a = 1.0;
        #elif TRANSLUCENT_MODE == 1
            color.rgb = c;
        #elif TRANSLUCENT_MODE == 2
            color.rgb = albedo * lightmap.y * sunColor * 0.2;
        #endif
    }

    gl_FragData[0] = color;
}

#endif

#ifdef VSH
flat out float isWater, isIce;

attribute vec4 mc_Entity;
attribute vec4 mc_midTexCoord;
attribute vec4 at_tangent;

void main() {
    gl_Position = ftransform();
    isWater = mc_Entity.x == 8 ? 1.0 : 0.0;
    isIce = mc_Entity.x == 79 ? 1.0 : 0.0;

    vViewPos = gl_ModelViewMatrix * gl_Vertex;
    vWorldPos = viewPosToWorldPos(vViewPos);

    #if MC_VERSION < 11400
        vWorldPos -= vec4(0.0, 2.0, 0.0, 0.0);
    #endif

    worldDis0 = length(vWorldPos.xyz);
    vMcPos = vec4(vWorldPos.xyz + cameraPosition, 1.0);

    vec3 N = normalize(gl_NormalMatrix * gl_Normal);
    vec3 B = normalize(gl_NormalMatrix * cross(at_tangent.xyz, gl_Normal.xyz) * at_tangent.w);
    vec3 T = normalize(gl_NormalMatrix * at_tangent.xyz);
    tbnMatrix = mat3(T, B, N);

    normalVO = N;
    normalWO = normalize(viewPosToWorldPos(vec4(N, 0.0)).xyz);

    sunViewDir = normalize(sunPosition);
    moonViewDir = normalize(moonPosition);
    lightViewDir = normalize(shadowLightPosition);

    sunWorldDir  = normalize(viewPosToWorldPos(vec4(sunPosition, 0.0)).xyz);
    moonWorldDir = normalize(viewPosToWorldPos(vec4(moonPosition, 0.0)).xyz);
    lightWorldDir = normalize(viewPosToWorldPos(vec4(shadowLightPosition, 0.0)).xyz);

    isNoon = saturate(dot(sunWorldDir, upWorldDir) * NOON_DURATION);
    isNight = saturate(dot(moonWorldDir, upWorldDir) * NIGHT_DURATION);
    sunRiseSet = saturate(1 - isNoon - isNight);

    isNoonS = saturate(dot(sunWorldDir, upWorldDir) * NOON_DURATION_SLOW);
    isNightS = saturate(dot(moonWorldDir, upWorldDir) * NIGHT_DURATION_SLOW);
    sunRiseSetS = saturate(1 - isNoonS - isNightS);

    sunColor = texelFetch(gaux4, sunColorUV, 0).rgb;
    skyColor = texelFetch(gaux4, skyColorUV, 0).rgb;

    lmcoord  = (gl_TextureMatrix[1] * gl_MultiTexCoord1).xy;
    texcoord = (gl_TextureMatrix[0] * gl_MultiTexCoord0).xy;
    glcolor = gl_Color;
}

#endif
