// ============================== Step 1: Color Prep ============================== //
#if MC_VERSION >= 11300
    #if LU_WATERCOLOR_MODE >= 2
        vec3 glColorM = glColor.rgb;

        #if LU_WATERCOLOR_MODE >= 3
            glColorM.g = max(glColorM.g, 0.39);
        #endif

        #ifdef LU_GBUFFERS_WATER
            translucentMultCalculated = true;
            translucentMult.rgb = normalize(lu_sqrt2(glColor.rgb));
            translucentMult.g *= 0.88;
        #endif

        glColorM = lu_sqrt1(glColorM) * vec3(1.0, 0.85, 0.8);
    #else
        vec3 glColorM = vec3(0.43, 0.6, 0.8);
    #endif

    #if LU_WATER_STYLE < 3
        vec3 colorPM = lu_pow2(colorP.rgb);
        color.rgb = colorPM * glColorM;
    #else
        vec3 colorPM = vec3(0.25);
        color.rgb = 0.375 * glColorM;
    #endif
#else
    #if LU_WATER_STYLE < 3
        color.rgb = mix(color.rgb, vec3(lu_GetLuminance(color.rgb)), 0.88);
        color.rgb = lu_pow2(color.rgb) * vec3(2.3, 3.5, 3.1) * 0.9;
    #else
        color.rgb = vec3(0.13, 0.2, 0.27);
    #endif
#endif

#ifdef LU_WATERCOLOR_CHANGED
    color.rgb *= vec3(LU_WATERCOLOR_RM, LU_WATERCOLOR_GM, LU_WATERCOLOR_BM);
#endif

// ============================== Deep Blue Water Tint ============================== //
{
    vec3 deepBlue = vec3(0.04, 0.12, 0.32);
    color.rgb = mix(color.rgb, color.rgb * deepBlue * 3.0, 0.85);
    color.rgb *= 0.55;
}
// ============================== End of Step 1 ============================== //

#define LU_PHYSICS_OCEAN_INJECTION
#if defined LU_GENERATED_NORMALS && (LU_WATER_STYLE >= 2 || defined PHYSICS_OCEAN) && !defined DH_WATER
    noGeneratedNormals = true;
#endif

#if defined LU_GBUFFERS_WATER || defined DH_WATER
    lmCoordM.y = min(lmCoord.y * 1.07, 1.0); // Iris/Sodium skylight inconsistency workaround

    float fresnel2 = lu_pow2(fresnel);
    float fresnel4 = lu_pow2(fresnel2);

    // ============================== Step 2: Water Normals ============================== //
    reflectMult = 1.0;

    #if LU_WATER_MAT_QUALITY >= 3
        materialMask = OSIEBCA * 241.0; // Water
    #endif

    #if LU_WATER_MAT_QUALITY >= 2 || LU_WATER_STYLE >= 2
        #define LU_WATER_SPEED_MULT_M LU_WATER_SPEED_MULT * 0.018
        float rawWind = frameTimeCounter * LU_WATER_SPEED_MULT_M;
        vec2 wind = vec2(rawWind, 0.0);
        vec3 worldPos = playerPos + cameraPosition;
        vec2 waterPos = worldPos.xz;
        #if LU_WATER_STYLE < 3 && defined LU_GBUFFERS_WATER
            float blockRes = absMidCoordPos.x * atlasSize.x * 2.0;
            waterPos = floor(waterPos * blockRes) / blockRes;
        #endif
        waterPos = 0.032 * (waterPos + worldPos.y * 2.0);
    #endif

    #if LU_WATER_STYLE >= 2 || LU_RAIN_PUDDLES >= 1 && LU_WATER_STYLE == 1 && LU_WATER_MAT_QUALITY >= 2
        vec3 normalMap = vec3(0.0, 0.0, 1.0);
        #if LU_WATER_STYLE >= 2
            vec2 waterPosM = waterPos;

            #if LU_WATER_SIZE_MULT != 100
                #define LU_WATER_SIZE_MULT_M LU_WATER_SIZE_MULT * 0.01
                waterPosM *= LU_WATER_SIZE_MULT_M;
            #endif

            #define LU_WATER_BUMPINESS_M LU_WATER_BUMPINESS * 0.8

            #if LU_WATER_STYLE >= 2
                waterPosM *= 2.5; wind *= 2.5;

                #if LU_WATER_MAT_QUALITY >= 2
                    vec2 parallaxMult = -0.01 * viewVector.xy / viewVector.z;
                    for (int i = 0; i < 4; i++) {
                        waterPosM += parallaxMult * texture2D(gaux4, waterPosM - wind).a;
                        waterPosM += parallaxMult * texture2D(gaux4, waterPosM * 0.25 - 0.5 * wind).a;
                    }
                #endif

                vec2 normalMed = texture2D(gaux4, waterPosM + wind).rg - 0.5;
                vec2 normalSmall = texture2D(gaux4, waterPosM * 4.0 - 2.0 * wind).rg - 0.5;
                vec2 normalBig = texture2D(gaux4, waterPosM * 0.25 - 0.5 * wind).rg - 0.5;
                     normalBig += texture2D(gaux4, waterPosM * 0.05 - 0.05 * wind).rg - 0.5;

                normalMap.xy = normalMed * LU_WATER_BUMP_MED + normalSmall * LU_WATER_BUMP_SMALL + normalBig * LU_WATER_BUMP_BIG;
                normalMap.xy *= 6.0 * (1.0 - 0.7 * fresnel) * LU_WATER_BUMPINESS_M;
            #endif

            normalMap.xy *= 0.03 * lmCoordM.y + 0.01;
        #else
            float pNormalMult = 0.02 * rainFactor * inRainy * lu_pow2(lmCoordM.y);

            if (pNormalMult > 0.0005) {
                vec2 puddlePos = floor((playerPos.xz + cameraPosition.xz) * 16.0) * 0.00625;

                vec2 puddleWind = vec2(frameTimeCounter) * 0.015;
                vec2 pNormalCoord1 = puddlePos + vec2(puddleWind.x, puddleWind.y);
                vec2 pNormalCoord2 = puddlePos + vec2(puddleWind.x * -1.5, puddleWind.y * -1.0);
                vec3 pNormalNoise1 = texture2D(noisetex, pNormalCoord1).rgb;
                vec3 pNormalNoise2 = texture2D(noisetex, pNormalCoord2).rgb;

                normalMap.xy = (pNormalNoise1.xy + pNormalNoise2.xy - vec2(1.0)) * pNormalMult;
        #endif

            normalMap.z = sqrt(1.0 - (lu_pow2(normalMap.x) + lu_pow2(normalMap.y)));
            normalM = clamp(normalize(normalMap * tbnMatrix), vec3(-1.0), vec3(1.0));

        #if LU_WATER_STYLE == 1
            }
        #endif

        #if LU_WATER_STYLE >= 2
            vec3 vector = reflect(nViewPos, normalize(normalM));
            float norMix = lu_pow2(lu_pow2(lu_pow2(1.0 - lu_max0(dot(normal, vector))))) * 0.5;
            normalM = mix(normalM, normal, norMix); // Fixes normals pointing inside water

            float fresnelP = fresnel;
            fresnel = clamp(1.0 + dot(normalM, nViewPos), 0.0, 1.0);
        #endif
    #endif
    // ============================== End of Step 2 ============================== //

    // ============================== Step 3: Water Material Features ============================== //
    #if LU_WATER_MAT_QUALITY >= 2
        if (isEyeInWater != 1) {
            // Noise Coloring //
            float noise = texture2D(noisetex, (waterPos + wind) * 0.25).g;
                  noise = noise - 0.5;
                  noise *= 0.25;
            color.rgb = pow(color.rgb, vec3(1.0 + noise));

            // Water Alpha //
            #ifdef LU_GBUFFERS_WATER
                float depthT = texelFetch(depthtex1, texelCoord, 0).r;
            #elif defined DH_WATER
                float depthT = texelFetch(dhDepthTex1, texelCoord, 0).r;
            #endif
            vec3 screenPosT = vec3(screenPos.xy, depthT);
            #ifdef LU_TAA
                vec3 viewPosT = lu_ScreenToView(vec3(lu_TAAJitter(screenPosT.xy, -0.5), screenPosT.z));
            #else
                vec3 viewPosT = lu_ScreenToView(screenPosT);
            #endif
            float lViewPosT = length(viewPosT);
            float lViewPosDifM = lViewPos - lViewPosT;

            #if LU_WATER_STYLE < 3
                color.a = lu_sqrt1(color.a);
            #else
                color.a = 0.98;
            #endif

            #ifdef DISTANT_HORIZONS
                if (depthT == 1.0) color.a *= smoothstep(far, far * 0.9, lViewPos);
            #endif

            #if LU_WATER_FOG_MULT != 100
                #define LU_WATER_FOG_MULT_M LU_WATER_FOG_MULT * 0.01;
                lViewPosDifM *= LU_WATER_FOG_MULT_M;
            #endif

            float waterFog = lu_max0(1.0 - exp(lViewPosDifM * 0.075));
            color.a *= 0.25 + 0.75 * waterFog;

            #if defined LU_BRIGHT_CAVE_WATER && LU_WATER_ALPHA_MULT < 200
                // For better water visibility in caves and some extra color pop outdoors
                color.rgb *= 2.5 - lu_sqrt2(waterFog) - 0.5 * lmCoordM.y;
            #endif

            #if LU_WATER_ALPHA_MULT != 100
                #define LU_WATER_ALPHA_MULT_M 100.0 / LU_WATER_ALPHA_MULT
                color.a = pow(color.a, LU_WATER_ALPHA_MULT_M);
            #endif
            ///

            // Water Foam //
            #if LU_WATER_FOAM_I > 0 && defined LU_GBUFFERS_WATER
                if (NdotU > 0.99) {
                    vec3 matrixM = vec3(
                        gbufferModelViewInverse[0].y,
                        gbufferModelViewInverse[1].y,
                        gbufferModelViewInverse[2].y
                    );
                    float playerPosTY = dot(matrixM, viewPosT) + gbufferModelViewInverse[3].y;
                    float yPosDif = playerPosTY - playerPos.y;

                    #if LU_WATER_STYLE < 3 && MC_VERSION >= 11300
                        float dotColorPM = dot(colorPM, colorPM);
                        float foamThreshold = min(lu_pow2(dotColorPM) * 1.6, 1.2);
                    #else
                        float foamThreshold = lu_pow2(texture2D(noisetex, waterPos * 4.0 + wind * 0.5).g) * 1.6;
                    #endif
                    float foam = lu_pow2(clamp((foamThreshold + yPosDif) / foamThreshold, 0.0, 1.0));
                    #ifndef END
                        foam *= 0.4 + 0.25 * lmCoord.y;
                    #else
                        foam *= 0.6;
                    #endif
                    foam *= clamp((fract(worldPos.y) - 0.7) * 10.0, 0.0, 1.0);

                    // dense white foam on edges
                    vec4 foamColor = vec4(1.0, 1.0, 1.0, 1.0);

                    #define LU_WATER_FOAM_IM LU_WATER_FOAM_I * 0.01
                    #if LU_WATER_FOAM_I < 100
                        foam *= LU_WATER_FOAM_IM;
                    #elif LU_WATER_FOAM_I > 100
                        foamColor *= LU_WATER_FOAM_IM;
                    #endif

                    color = mix(color, foamColor, foam);
                    reflectMult = 1.0 - foam;
                }
            #endif
            ///
        } else { // Underwater
            noDirectionalShading = true;

            reflectMult = 0.5;

            #if MC_VERSION < 11300 && LU_WATER_STYLE >= 3
                color.a = 0.7;
            #endif

            #ifdef LU_GBUFFERS_WATER
                #if LU_WATER_STYLE == 1
                    translucentMult.rgb *= 1.0 - fresnel4;
                #else
                    translucentMult.rgb *= 1.0 - 0.9 * max(0.5 * sqrt(fresnel4), fresnel4);
                #endif
            #endif
        }
    #else
        shadowMult = vec3(0.0);
    #endif
    // ============================== End of Step 3 ============================== //

    // ============================== Step 4: Final Tweaks ============================== //
    reflectMult *= 0.5 + 0.5 * NdotUmax0;

    color.a = mix(color.a, 1.0, fresnel4);

    #ifdef LU_GBUFFERS_WATER
        #if LU_WATER_STYLE == 3 || LU_WATER_STYLE == 2 && LU_SUN_MOON_STYLE >= 2
            smoothnessG = 1.0;

            const float WATER_BUMPINESS_M2 = min(LU_WATER_BUMP_MED * LU_WATER_BUMP_SMALL * LU_WATER_BUMPINESS * 0.65, 1.0);
            vec2 lightNormalP = WATER_BUMPINESS_M2 * (normalMed + 0.5 * normalSmall);
            vec3 lightNormal = normalize(vec3(lightNormalP, 1.0) * tbnMatrix);
            highlightMult = dot(lightNormal, lightVec);
            highlightMult = lu_max0(highlightMult) / max(dot(normal, lightVec), 0.17);
            highlightMult = mix(lu_pow2(lu_pow2(highlightMult * 1.1)), 1.0, lu_min1(sqrt(miplevel) * 0.45)) * 0.24;
        #else
            smoothnessG = 0.5;

            highlightMult = min(lu_pow2(lu_pow2(dot(colorP.rgb, colorP.rgb) * 0.4)), 0.5);
            highlightMult *= (16.0 - 15.0 * fresnel2) * (sunVisibility > 0.5 ? 0.85 : 0.425);
        #endif
    #endif
    // ============================== End of Step 4 ============================== //
#endif
