

#include "/lib/taa/res_params.glsl"
varying vec4 color;
varying float NdotL;
uniform vec3 atmosphereUp;
flat varying vec3 torch;
varying vec4 lmtexcoord;
varying vec3 viewVector;
uniform sampler2DShadow shadow;
uniform sampler2DShadow shadowtex0;
varying vec3 normal1;

varying float mat;

uniform vec3 cameraPosition;
uniform sampler2D texture;

uniform mat4 gbufferModelViewInverse;
uniform mat4 shadowModelView;
uniform mat4 shadowProjection;
uniform float nightblendShadows;
uniform float far;
flat varying vec3 ambientUp;
flat varying vec3 ambientDown;
flat varying vec3 ambientLeft;
flat varying vec3 ambientRight;
flat varying vec3 ambientB;
flat varying vec3 ambientF;
flat varying float ambientA;
flat varying vec4 sunColor;

#include "/lib/color_transforms.glsl"
#include "/lib/uniforms.glsl"
#include "/lib/shadow_param.glsl"
uniform float darknessLightFactor;
#include "/lib/light.glsl"

#define diagonal3(m) vec3((m)[0].x, (m)[1].y, m[2].z)
#define projMAD(m, v) (diagonal3(m) * (v) + (m)[3].xyz)
float R2_dither3()
{
    vec2 alpha = vec2(0.75487765, 0.56984026);
    return fract(alpha.x * gl_FragCoord.x + alpha.y * gl_FragCoord.y + 1.0 / 1.6180339887 * frameCounter);
}

vec2 tapLocation(int sampleNumber, int nb, float nbRot, float jitter, float distort)
{
    float alpha = (sampleNumber + jitter) / nb;
    float angle = jitter * 6.28 + alpha * nbRot * 6.28;

    float sin_v, cos_v;

    sin_v = sin(angle);
    cos_v = cos(angle);

    return vec2(cos_v, sin_v) * sqrt(alpha);
}

// https://gitlab.com/jeseibel/distant-horizons-core/-/blob/main/core/src/main/resources/shaders/flat_shaded.frag?ref_type=heads
// Property of Distant Horizons [mod]

const int noiseSteps = 4;
const float noiseIntensity = 5.0;
const int noiseDropoff = 1024;

float rand(float co)
{
    return fract(sin(co * (91.3458)) * 47453.5453);
}
float rand(vec2 co)
{
    return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);
}
float rand(vec3 co)
{
    return rand(co.xy + rand(co.z));
}

vec3 quantize(const in vec3 val, const in int stepSize)
{
    return floor(val * stepSize) / stepSize;
}

void applyNoise(inout vec4 fragColor, const in vec3 viewPos, const in float viewDist)
{
    float noiseAmplification = noiseIntensity * 0.01;
    float lum = (fragColor.r + fragColor.g + fragColor.b) / 3.0;
    noiseAmplification =
        (1.0 - pow(lum * 2.0 - 1.0, 2.0)) * noiseAmplification; // Lessen the effect on depending on how dark the object
                                                                // is, equasion for this is -(2x-1)^{2}+1
    noiseAmplification *= fragColor.a;                          // The effect would lessen on transparent objects

    // Random value for each position
    float randomValue = rand(quantize(viewPos, noiseSteps));
    randomValue = randomValue * 2.0 * noiseAmplification - noiseAmplification;

    // Modifies the color
    // A value of 0 on the randomValue will result in the original color, while a value of 1 will result in a fully
    // bright color
    vec3 newCol = fragColor.rgb + (1.0 - fragColor.rgb) * randomValue;
    newCol = clamp(newCol, 0, 1);

    if (noiseDropoff != 0)
    {
        float distF = min(viewDist / noiseDropoff, 1.0);
        newCol = mix(newCol, fragColor.rgb, distF); // The further away it gets, the less noise gets applied
    }

    fragColor.rgb = newCol;
}

float shadowFunc(float diffuseSun, float sssAmount, vec3 p3, float noise, vec2 lightmap, vec3 fragpos)
{
    float shading = 0.0;
    vec3 ProjShadowPos = (mat3(shadowModelView) * p3) + shadowModelView[3].xyz;

    ProjShadowPos = vec3(ProjShadowPos.x * shadowProjection[0].x + shadowProjection[3].x,
                         ProjShadowPos.y * shadowProjection[1].y + shadowProjection[3].y,
                         ProjShadowPos.z * shadowProjection[2].z + shadowProjection[3].z);

    float distortFac = calcDistort(ProjShadowPos.xy);

    // ProjShadowPos.xy *= distortFac2;
    ProjShadowPos.xy *= distortFac;

    ProjShadowPos = ProjShadowPos * vec3(0.5, 0.5, 0.5 / 6) + 0.5;
    diffuseSun = mix(diffuseSun, 0.5, sssAmount);
    float scaleFac1 = mix(distortFac, noise * 20.0, float(sssAmount > 0.0));

    float scaleFac = 1.0 / (scaleFac1 * 6000.0);
    float bias = mix(0.0001, 0.0, length(fragpos) / dhFarPlane);
    ProjShadowPos.z -= scaleFac + bias;
    shading = texture(shadow, ProjShadowPos);

    shading = mix(1.0, shading, nightblendShadows);
    diffuseSun = diffuseSun * shading * lightmap.y;
    diffuseSun *= lightmap.y * (1 - rainStrength * 0.9);

    return diffuseSun;
}
vec3 CookTorranceBRDF(vec3 normal, vec3 viewDir, vec3 lightDir, float perceptualSmoothness, vec3 F0)
{
    normal = normalize(normal);
    viewDir = normalize(viewDir);
    lightDir = normalize(lightDir);

    vec3 halfVec = normalize(lightDir + viewDir);

    float NoL = max(dot(normal, lightDir), 0.0);
    float NoV = max(dot(normal, viewDir), 0.0);
    float NoH = max(dot(normal, halfVec), 0.0);
    float VoH = max(dot(viewDir, halfVec), 0.0);

    // Handle cases where NoV is very small
    if (NoV <= 0.0)
    {
        return vec3(0.0); // No reflection if the view direction is below the surface
    }

    // Convert perceptual smoothness to roughness
    float roughness = clamp(pow(1.0 - perceptualSmoothness, 2.0), 0.0001, 0.9999);

    // Distribution function (GGX)
    // float alpha = roughness * roughness;
    float alpha = roughness;
    float alpha2 = alpha * alpha;
    float denom = NoH * NoH * (alpha2 - 1.0) + 1.0;
    float D = alpha2 / (PI * denom * denom);

    // Fresnel function (Schlick's approximation)
    vec3 F = F0 + (1.0 - F0) * pow(1.0 - VoH, 5.0);

    // Geometry function (Smith's method with GGX)
    float k = alpha / 2.0;
    float G1 = NoL / (NoL * (1.0 - k) + k);
    float G2 = NoV / (NoV * (1.0 - k) + k);
    float G = G1 * G2;

    // Cook-Torrance BRDF
    vec3 specular = (D * F * G) / (4.0 * NoL * NoV + 0.0001);

    return clamp(specular, 0, 1.0);
}

/* DRAWBUFFERS:5*/
void main()
{
    vec3 fragpos = toScreenSpaceDH(gl_FragCoord.xyz * vec3(texelSize, 1.0));
    float viewDist = length(fragpos);
    // if (length(viewDist) / far < R2_dither3() / 2)
    //     discard;
    vec4 color = color;
    vec3 prenoise = color.rgb;
    vec3 p3 = mat3(gbufferModelViewInverse) * fragpos + gbufferModelViewInverse[3].xyz;
    float sss = mat * 0.6 * R2_dither3();
    float diffuseSun = max(dot(normal1, lightPos), 0.0);
    // diffuseSun = max(diffuseSun, mat);
    vec3 worldPos = p3 + cameraPosition;
#ifdef DH_SHADOWMAP
    diffuseSun = shadowFunc(diffuseSun, sss, p3, R2_dither3(), lmtexcoord.zw, fragpos);
#else
    diffuseSun *= clamp((pow(lmtexcoord.w, 16) * 2 - 0.5) * 2, 0, 1);

#endif

    applyNoise(color, worldPos, viewDist);

    float lumaValue = luma(prenoise.rgb);
    float lumaLod = luma(color.rgb);
    float modifier = max(lumaValue / lumaLod, 0.0);

    //    vec4(0.25 * modifier, 0.035 * modifier, translucent, lumaValue * clamp(lumaValue * 2.0 - 0.5, 0.0, 1.0));
    float reflectance = 0.025 * modifier;
    float smoothness = 0.1 * modifier;

    vec4 materials = vec4(sss, 0, reflectance, smoothness);

    vec3 specular =
        CookTorranceBRDF(normal1, -p3, lightPos, materials.w, vec3(materials.z)) * diffuseSun * sunLight * 0.5;

    vec3 ambient = calcAmbientLight(normal1, lmtexcoord.zw, ambientUp, ambientDown, ambientRight, ambientLeft, ambientB,
                                    ambientF, vec3(0.6 * ambientA * lmtexcoord.w), diffuseSun, torch);
    color.rgb = ambient * toLinear(color.rgb) + specular;

    gl_FragData[0] = vec4(color);
}
