/////////////////////////////////////
// Unbound Visual + PT v9.6 by EminGT //
/////////////////////////////////////

//Common//
#include "/lib/common.glsl"

//////////Fragment Shader//////////Fragment Shader//////////Fragment Shader//////////
#ifdef FRAGMENT_SHADER

noperspective in vec2 texCoord;

//Pipeline Constants//
#include "/lib/pipelineSettings.glsl"

//Common Variables//
vec2 view = vec2(viewWidth, viewHeight);

#ifdef MC_ANISOTROPIC_FILTERING
    #define OPTIFINE_AF_ERROR
#endif

#if COLORED_LIGHTING > 0 && defined MC_OS_MAC
    #define APPLE_ACT_ERROR
#endif

#if COLORED_LIGHTING > 0 && (!defined IS_IRIS || !defined IRIS_FEATURE_CUSTOM_IMAGES)
    #define OPTIFINE_ACT_ERROR
#endif

#if COLORED_LIGHTING > 0
    #define COORDINATES_ACT_ERROR
    // SHADOWDISTANCE_ACT_ERROR intentionally disabled: lightVoxelization.glsl
    // already clamps the effective ACT distance to shadowDistance*2 at
    // runtime (see "effectiveACTdistance"), so setting ACT higher than that
    // is just wasted GPU budget, not a broken/incorrect render. Removed the
    // hard red warning screen so tweaking Performance sliders doesn't get
    // interrupted by it.
#endif

#if WORLD_SPACE_REFLECTIONS > 0 && COLORED_LIGHTING == 0
    #define WSR_MISSING_ACT_ERROR
#endif

#if defined VOXY && defined DISTANT_HORIZONS
    #define MULTIPLE_LOD_MODS_ERROR
#endif

#if defined MC_ANISOTROPIC_FILTERING || COLORED_LIGHTING > 0 || defined WSR_MISSING_ACT_ERROR || defined MULTIPLE_LOD_MODS_ERROR
    #define ANY_ERROR_MESSAGE
#endif

#if WORLD_SPACE_REFLECTIONS_INTERNAL > 0 && WORLD_SPACE_PLAYER_REF == 1
    #include "/lib/voxelization/SSBOs/clearSSBOs.glsl"
#endif

//Common Functions//
#if IMAGE_SHARPENING > 0
    vec2 viewD = 1.0 / vec2(viewWidth, viewHeight);

    vec2 sharpenOffsets[4] = vec2[4](
        vec2( viewD.x,  0.0),
        vec2( 0.0,  viewD.x),
        vec2(-viewD.x,  0.0),
        vec2( 0.0, -viewD.x)
    );

    void SharpenImage(inout vec3 color, vec2 texCoordM) {
        #ifdef TAA
            float sharpenMult = IMAGE_SHARPENING;
        #else
            float sharpenMult = IMAGE_SHARPENING * 0.5;
        #endif
        float mult = 0.0125 * sharpenMult;
        color *= 1.0 + 0.05 * sharpenMult;

        for (int i = 0; i < 4; i++) {
            color -= texture2D(colortex3, texCoordM + sharpenOffsets[i]).rgb * mult;
        }
    }
#endif

//Includes//
#ifdef ANY_ERROR_MESSAGE
    #include "/lib/textRendering/textRenderer.glsl"

    void beginTextM(int textSize, vec2 offset) {
        float scale = 860;
        beginText(ivec2(vec2(scale * viewWidth / viewHeight, scale) * texCoord) / textSize, ivec2(0 + offset.x, scale / textSize - offset.y));
        text.bgCol = vec4(0.0);
    }
#endif

//Program//
void main() {
    vec2 texCoordM = texCoord;

    #ifdef UNDERWATER_DISTORTION
        if (isEyeInWater == 1) {
            vec2 waterCoord = texCoord * 4.0 + frameTimeCounter * 0.5;
            float wave = sin(waterCoord.x * 12.0 + waterCoord.y * 8.0 + frameTimeCounter * 2.0);
            wave += sin(waterCoord.y * 15.0 - waterCoord.x * 10.0 + frameTimeCounter * 1.5);
            texCoordM += WATER_REFRACTION_INTENSITY * 0.0006 * wave;
        } else {
            float waterFade = max(0.0, 1.0 - eyeBrightnessM * 2.0);
            if (waterFade > 0.01) {
                vec2 dropCoord = texCoord * 20.0 + frameTimeCounter * 0.1;
                float drop = texture2DLod(noisetex, dropCoord, 0.0).r;
                drop = pow(drop, 3.0) * 0.5;
                texCoordM += drop * waterFade * 0.002;
            }
            #ifdef PUDDLE_VOXELIZATION
                if (rainFactor > 0.01) {
                    vec2 rainDropCoord = texCoord * 15.0 + frameTimeCounter * 0.15;
                    float rainDrop = texture2DLod(noisetex, rainDropCoord, 0.0).r;
                    rainDrop = pow(rainDrop, 4.0) * 1.5;
                    float drop2 = texture2DLod(noisetex, rainDropCoord + 50.0, 0.0).g;
                    drop2 = pow(drop2, 4.0) * 1.5;
                    texCoordM += (rainDrop + drop2) * rainFactor * 0.003;
                }
            #endif
        }
    #endif

    vec3 color = texture2D(colortex3, texCoordM).rgb;

    #if CHROMA_ABERRATION > 0
        vec2 scale = vec2(1.0, viewHeight / viewWidth);
        vec2 aberration = (texCoordM - 0.5) * (2.0 / vec2(viewWidth, viewHeight)) * scale * CHROMA_ABERRATION;
        color.rb = vec2(texture2D(colortex3, texCoordM + aberration).r, texture2D(colortex3, texCoordM - aberration).b);
    #endif

    #if IMAGE_SHARPENING > 0
        SharpenImage(color, texCoordM);
    #endif

    #ifdef UNDERWATER_DISTORTION
        if (rainFactor > 0.01) {
            vec2 rainBlurCoord = texCoord * 6.0 + frameTimeCounter * 0.05;
            float rainBlur = texture2DLod(noisetex, rainBlurCoord, 0.0).r;
            vec2 rainBlurOffset = vec2((rainBlur - 0.5) * rainFactor * 0.001);
            vec3 blurColor = texture2D(colortex3, texCoordM + rainBlurOffset).rgb;
            blurColor += texture2D(colortex3, texCoordM - rainBlurOffset).rgb;
            color = mix(color, blurColor * 0.5, rainFactor * 0.15);
        }
    #endif

    // v9.0: Rain screen distortion - water droplets + radial blur
    if (rainFactor > 0.01 && isEyeInWater == 0) {
        vec2 rainDistortCoord = texCoord * 25.0 + frameTimeCounter * 0.12;
        float rainNoise = texture2DLod(noisetex, rainDistortCoord, 0.0).r;
        float rainNoise2 = texture2DLod(noisetex, rainDistortCoord * 1.3 + 50.0, 0.0).g;
        
        // Water droplet distortion - wavy screen offset
        vec2 dropletOffset = vec2(
            sin(rainNoise * 6.28 + frameTimeCounter * 2.0) * 0.003,
            cos(rainNoise2 * 6.28 + frameTimeCounter * 1.5) * 0.002
        ) * rainFactor;
        
        color.rgb = texture2D(colortex3, texCoordM + dropletOffset).rgb;
        
        // Slight radial blur from center (lens wet effect)
        float distFromCenter = length(texCoord - 0.5) * 2.0;
        float radialBlur = distFromCenter * rainFactor * 0.002;
        vec2 radialDir = normalize(texCoord - 0.5) * radialBlur;
        vec3 blurSample = texture2D(colortex3, texCoordM + radialDir).rgb;
        blurSample += texture2D(colortex3, texCoordM - radialDir).rgb;
        color.rgb = mix(color.rgb, blurSample * 0.5, rainFactor * 0.2);
    }

    // v9.0: Thunder border blur - circular blur on screen edges during thunder
    if (rainFactor > 0.3) {
        // Create random thunder flash using noise
        float thunderTime = floor(frameTimeCounter * 0.5);
        float thunderHash = fract(sin(thunderTime * 127.1) * 43758.5453);
        float thunderFlash = step(0.97, thunderHash) * rainFactor; // ~3% chance per half-second
        
        if (thunderFlash > 0.01) {
            float distFromCenter = length(texCoord - 0.5) * 2.0;
            float borderMask = smoothstep(0.3, 0.9, distFromCenter); // only edges
            
            // Circular blur on borders
            float blurStrength = borderMask * thunderFlash * 0.008;
            vec2 blurDir = normalize(texCoord - 0.5) * blurStrength;
            
            vec3 thunderBlur = vec3(0.0);
            thunderBlur += texture2D(colortex3, texCoordM + blurDir).rgb;
            thunderBlur += texture2D(colortex3, texCoordM - blurDir).rgb;
            thunderBlur += texture2D(colortex3, texCoordM + blurDir * 0.5).rgb;
            thunderBlur += texture2D(colortex3, texCoordM - blurDir * 0.5).rgb;
            thunderBlur *= 0.25;
            
            color.rgb = mix(color.rgb, thunderBlur, borderMask * thunderFlash * 0.6);
            
            // Slight white flash on borders
            color.rgb += vec3(0.05, 0.06, 0.08) * borderMask * thunderFlash;
        }
    }

    // v9.0: Rain/cloudy weather - slight darkening + desaturation
    if (rainFactor > 0.01 && isEyeInWater == 0) {
        // Slight darkening (not as dark as night)
        float rainDarken = 1.0 - rainFactor * 0.15; // max 15% darker
        color.rgb *= rainDarken;
        
        // Slight desaturation (reduce by ~10-20)
        float lum = dot(color.rgb, vec3(0.299, 0.587, 0.114));
        float desat = rainFactor * 0.15; // max 15% desaturation
        color.rgb = mix(color.rgb, vec3(lum), desat);
    }

    /*ivec2 boxOffsets[8] = ivec2[8](
        ivec2( 1, 0),
        ivec2( 0, 1),
        ivec2(-1, 0),
        ivec2( 0,-1),
        ivec2( 1, 1),
        ivec2( 1,-1),
        ivec2(-1, 1),
        ivec2(-1,-1)
    );

    for (int i = 0; i < 8; i++) {
        color = max(color, texelFetch(colortex3, texelCoord + boxOffsets[i], 0).rgb);
    }*/

    #ifdef MULTIPLE_LOD_MODS_ERROR
        #include "/lib/textRendering/error_multiple_lod_mods.glsl"
    #elif defined OPTIFINE_AF_ERROR
        #include "/lib/textRendering/error_optifine_af.glsl"
    #elif defined APPLE_ACT_ERROR
        #include "/lib/textRendering/error_apple_act.glsl"
    #elif defined OPTIFINE_ACT_ERROR
        #include "/lib/textRendering/error_optifine_act.glsl"
    #elif defined WSR_MISSING_ACT_ERROR
        #include "/lib/textRendering/error_wsr_missing_act.glsl"
    #else
        #ifdef COORDINATES_ACT_ERROR
            ivec2 absCameraPositionIntXZ = abs(cameraPositionInt.xz);
            if (max(absCameraPositionIntXZ.x, absCameraPositionIntXZ.y) > 8388550) {
                #include "/lib/textRendering/error_coordinates_act.glsl"
            }
        #endif
        #ifdef SHADOWDISTANCE_ACT_ERROR
            if (COLORED_LIGHTING_INTERNAL > shadowDistance*2) {
                #include "/lib/textRendering/error_shadowdistance_act.glsl"
            }
        #endif
    #endif

    #if WORLD_SPACE_REFLECTIONS_INTERNAL > 0 && WORLD_SPACE_PLAYER_REF == 1
        clearSSBOs();
    #endif

    #ifdef VIGNETTE_R
        vec2 texCoordMin = texCoordM.xy - 0.5;
        float vignette = 1.0 - dot(texCoordMin, texCoordMin) * (1.0 - GetLuminance(color));
        color *= vignette;
    #endif

    // ("Unbound Visual +" fix - hilangkan noise bintik-bintik film): dither
    // dilemahkan (dibagi 128 -> 192) supaya butiran halus di layar tidak
    // terlalu kelihatan, khususnya di area gelap/bawah air, tapi masih
    // cukup untuk mencegah color banding.
    float dither = texture2DLod(noisetex, texCoord * view / 128.0, 0.0).b;
    color += vec3((dither - 0.25) / 192.0);

    #if CINEMATIC_BORDER == 1
        {
            // Crop the current window aspect down to CINEMATIC_BORDER_RATIO
            // (16:9 by default) with solid black bars. Whichever axis the
            // real window is "too big" on relative to the target ratio gets
            // bars - ultrawide/near-square windows get pillarboxed (bars on
            // the sides), tall/narrow windows get letterboxed (bars on top
            // and bottom) - so it behaves correctly regardless of the
            // player's actual window/monitor shape, not just 16:9 monitors.
            float screenAspect = view.x / view.y;
            float barMask = 1.0;

            if (screenAspect > CINEMATIC_BORDER_RATIO) {
                // Window wider than target -> bars on left/right
                float scaledWidth = view.y * CINEMATIC_BORDER_RATIO;
                float barPx = (view.x - scaledWidth) * 0.5;
                float distFromEdge = min(gl_FragCoord.x, view.x - gl_FragCoord.x);
                barMask = clamp((distFromEdge - barPx) / max(CINEMATIC_BORDER_FEATHER, 0.001) + 1.0, 0.0, 1.0);
            } else if (screenAspect < CINEMATIC_BORDER_RATIO) {
                // Window taller than target -> bars on top/bottom
                float scaledHeight = view.x / CINEMATIC_BORDER_RATIO;
                float barPx = (view.y - scaledHeight) * 0.5;
                float distFromEdge = min(gl_FragCoord.y, view.y - gl_FragCoord.y);
                barMask = clamp((distFromEdge - barPx) / max(CINEMATIC_BORDER_FEATHER, 0.001) + 1.0, 0.0, 1.0);
            }

            color *= barMask;
        }
    #endif

    #ifdef UNDERWATER_DISTORTION
        float waterLens = float(isEyeInWater == 1);
        float exitFade = max(0.0, 1.0 - eyeBrightnessM * 3.0);
        waterLens = max(waterLens, exitFade);
        // v9.0: Further reduced underwater lens grain for cleaner ocean exploration
        if (waterLens > 0.01) {
            vec2 lensCoord = texCoord * 8.0 + frameTimeCounter * 0.2;
            float film = texture2DLod(noisetex, lensCoord, 0.0).g;
            film = film * film * 0.03 * waterLens; // v9.0: reduced from 0.07 for cleaner underwater
            color = mix(color, color * (0.97 + 0.03 * waterLens), film);
        }
        if (rainFactor > 0.01) {
            vec2 rainCoord = texCoord * 12.0 + frameTimeCounter * 0.3;
            float rainFilm = texture2DLod(noisetex, rainCoord, 0.0).b;
            float rainFilm2 = texture2DLod(noisetex, rainCoord * 1.7 + 100.0, 0.0).r;
            rainFilm = rainFilm * rainFilm * 0.4 * rainFactor;
            rainFilm2 = rainFilm2 * rainFilm2 * 0.3 * rainFactor;
            color = mix(color, color * 0.92, rainFilm + rainFilm2);
        }
    #endif

    /* DRAWBUFFERS:0 */
    gl_FragData[0] = vec4(color, 1.0);
}

#endif

//////////Vertex Shader//////////Vertex Shader//////////Vertex Shader//////////
#ifdef VERTEX_SHADER

noperspective out vec2 texCoord;

//Attributes//

//Common Variables//

//Common Functions//

//Includes//

//Program//
void main() {
    gl_Position = ftransform();
    texCoord = (gl_TextureMatrix[0] * gl_MultiTexCoord0).xy;
}

#endif