//     _________      __        __     ___       __     __________      ________        ______        __           
//    /  _____  \    |  |      |  |   |   \     |  |   |   _____  \    |__    __|      /  __  \      |  |          
//   /  /     \__\   |  |      |  |   |    \    |  |   |  |     \  \      |  |        /  /  \  \     |  |          
//  |  |             |  |      |  |   |  |  \   |  |   |  |      |  |     |  |       /  /    \  \    |  |          
//   \  \______      |  |      |  |   |  |\  \  |  |   |  |      |  |     |  |      |  |______|  |   |  |          
//    \______  \     |  |      |  |   |  | \  \ |  |   |  |      |  |     |  |      |   ______   |   |  |          
//           \  \    |  |      |  |   |  |  \  \|  |   |  |      |  |     |  |      |  |      |  |   |  |          
//  ___       |  |   |  |      |  |   |  |   \  |  |   |  |      |  |     |  |      |  |      |  |   |  |          
//  \  \_____/  /     \  \____/  /    |  |    \    |   |  |_____/  /    __|  |__    |  |      |  |   |  |_________ 
//   \_________/       \________/     |__|     \___|   |__________/    |________|   |__|      |__|   |____________|
//
//  General Public License v3.0. Copyright (C) 2026 GeForceLegend.
//  https://github.com/GeForceLegend/Sundial-Lite
//  https://www.gnu.org/licenses/gpl-3.0.en.html
//
//  Shadow
//

layout(location = 0) out vec4 shadowColor0;

in vec4 color;              // Will become vec4(worldPos, causticsStrength) when rendering water
in vec3 worldNormal;
in vec2 texcoord;
in float distortFixValue;

// #define SHADOW_DISTORTION_FIX

#include "/settings/GlobalSettings.glsl"
#include "/libs/Uniform.glsl"
#include "/libs/Common.glsl"

uniform sampler2D gaux1;

const int shadowMapResolution = 2048; // [1024 2048 4096 8192 16384]
const float realShadowMapResolution = shadowMapResolution * MC_SHADOW_QUALITY;

float waterCaustic(vec3 mcPos, vec3 lightDir) {

    float causticStrength = 0.5;

    #ifdef WATER_CAUSTIC
        vec3 causticPos = mcPos + vec3(1.0, 0.0, 0.5) * frameTimeCounter * WATER_WAVE_SPEED;
        vec2 causticCoord = causticPos.xz + causticPos.y;

        vec3 position = vec3(causticCoord, frameTimeCounter);

        const mat3 rotation = mat3(
            -0.6666667,-0.3333333, 0.6666667,
             0.6666667,-0.6666667, 0.3333333,
             0.3333333, 0.6666667, 0.6666667
        );

        float dist = 1.0;

        for (int i = 0; i < 4; i++) {
            position = rotation * position * 0.9 + 114.514;
            vec3 offset = 0.5 - fract(position);
            dist = min(dist, dot(offset, offset));
        }

        causticStrength = clamp(dist * sqrt(dist) * (3.0 - 2.0 * dist) * 0.9 + 0.1, 0.0, 1.0);
    #endif

    return causticStrength;
}

vec3 shadowCoordToWorldPos(vec3 shadowCoord) {
    float clipLengthInv = inversesqrt(dot(shadowCoord.xy, shadowCoord.xy));
    float shadowDistortion = pow(distortionStrength + 1.0, 1.0 / clipLengthInv) / distortionStrength - 1.0 / distortionStrength;
    shadowCoord.xy *= shadowDistortion * clipLengthInv;

    vec3 shadowViewPos = vec3(shadowProjectionInverse[0].x, shadowProjectionInverse[1].y, shadowProjectionInverse[2].z) * shadowCoord + shadowProjectionInverse[3].xyz;
    vec3 shadowPos = mat3(shadowModelViewInverse) * shadowViewPos + shadowModelViewInverse[3].xyz;
    return shadowPos;
}

void main() {
    #ifdef SHADOW_AND_SKY
        vec4 albedo = textureLod(gtexture, texcoord, 0.0);
        albedo *= abs(color);
        #ifdef COLORWHEEL
            vec2 lmcoord;
            float ao;
            vec4 overlayColor;
            clrwl_computeFragment(albedo, albedo, lmcoord, ao, overlayColor);
            albedo.rgb = mix(albedo.rgb, overlayColor.rgb, overlayColor.a);
        #endif

        vec2 shadowOffset = vec2(-float(abs(color.a + 0.5) <= 0.5), -float(color.a < -1.0)) * 0.5 * realShadowMapResolution;
        vec2 centerTexelOffset = gl_FragCoord.st - realShadowMapResolution * 0.75 - shadowOffset;
        if (any(greaterThan(abs(centerTexelOffset), vec2(realShadowMapResolution * 0.25))) || fwidth(shadowOffset.x) > 0.0 || albedo.w < max(0.01, alphaTestRef)) discard;

        if (shadowOffset.y < -0.5) {
            vec3 mcPos = color.xyz + cameraPosition;
            mcPos.y += 128.0;
            float floorMcHeight = floor(mcPos.y / 2.0);
            float caustic = waterCaustic(mcPos, shadowDirection);
            albedo = vec4(sqrt(caustic * (-color.a - 1.0)), mcPos.y * 0.5 - floorMcHeight, 1.0 - floorMcHeight / 255.0, 0.5);
        }
        if (shadowOffset.x < -0.5) {
            albedo.rgb = pow(
                albedo.rgb * (1.0 - 0.5 * pow2(albedo.w)),
                vec3(sqrt(albedo.w * 2.2 * 2.2 * 1.5))
            );
            albedo = vec4(sqrt(albedo.rgb), 1.0);
        }

        #ifdef SHADOW_DISTORTION_FIX
            vec3 shadowProjPos = vec3(centerTexelOffset / (realShadowMapResolution * 0.25), gl_FragCoord.z * 10.0 - 5.0);
            vec3 pixelWorldPos = shadowCoordToWorldPos(shadowProjPos);
            float pixelDistanceToFace = distortFixValue - dot(pixelWorldPos, worldNormal);
            float NdotL = dot(worldNormal, shadowDirection);
            float offsetLength = signMul(pixelDistanceToFace / max(1e-5, abs(NdotL)), NdotL);
            gl_FragDepth = gl_FragCoord.z + offsetLength * shadowProjection[2].z * 0.1;
        #endif
        shadowColor0 = albedo;
    #else
        discard;
    #endif
}

/* DRAWBUFFERS:0 */
