
#include "/lib/taa/res_params.glsl"
#include "/lib/uniforms.glsl"

varying vec2 texcoord;

uniform sampler2D colortex0;
uniform sampler2D colortex1;

uniform sampler2D colortex9;
uniform sampler2D depthtex0;

#include "/lib/color_transforms.glsl"
#include "/lib/projections.glsl"

vec3 tonemap(vec3 col)
{
    return col / (1 + luma(col));
}
vec3 invTonemap(vec3 col)
{
    return col / (1 - luma(col));
}

float MinDepth3x3(vec2 uv)
{
    ivec2 fragCoord = ivec2(gl_FragCoord.xy);
    float depth = texelFetch(depthtex0, fragCoord, 0).x;

    for (int x = -1; x <= 1; ++x)
    {
        for (int y = -1; y <= 1; ++y)
        {
            if (x != 0 || y != 0)
            {
                depth = min(depth, texelFetch(depthtex0, fragCoord + ivec2(x, y), 0).x);
            }
        }
    }

    return depth;
}
float MinDepth3x3DH(vec2 uv)
{
    ivec2 fragCoord = ivec2(gl_FragCoord.xy);
    float depth = texelFetch(dhDepthTex0, fragCoord, 0).x;

    for (int x = -1; x <= 1; ++x)
    {
        for (int y = -1; y <= 1; ++y)
        {
            if (x != 0 || y != 0)
            {
                depth = min(depth, texelFetch(dhDepthTex0, fragCoord + ivec2(x, y), 0).x);
            }
        }
    }

    return depth;
}

vec3 ReprojectDH(vec3 position)
{
    bool isHand = position.z < 0.6;
    if (position.z < 1.0 && !isHand)
    {
        position = ViewSpaceFromScreenSpace(position, dhProjectionInverse);
        position = mat3(gbufferModelViewInverse) * position + gbufferModelViewInverse[3].xyz;
        position += (cameraPosition - previousCameraPosition);
        position = mat3(gbufferPreviousModelView) * position + gbufferPreviousModelView[3].xyz;
        position = ScreenSpaceFromViewSpace(position, dhPreviousProjection);
    }
    else
    {
        position = ViewSpaceFromScreenSpace(position, dhProjectionInverse);
        position = mat3(gbufferModelViewInverse) * position + gbufferModelViewInverse[3].xyz;
        position = mat3(gbufferPreviousModelView) * position + gbufferPreviousModelView[3].xyz;
        position = ScreenSpaceFromViewSpace(position, dhPreviousProjection);
    }

    return position;
}
vec3 Reproject(vec3 position)
{
    bool isHand = position.z < 0.6;
    if (position.z < 1.0 && !isHand)
    {
        position = ViewSpaceFromScreenSpace(position, gbufferProjectionInverse);
        position = mat3(gbufferModelViewInverse) * position + gbufferModelViewInverse[3].xyz;
        position += (cameraPosition - previousCameraPosition);
        position = mat3(gbufferPreviousModelView) * position + gbufferPreviousModelView[3].xyz;
        position = ScreenSpaceFromViewSpace(position, gbufferPreviousProjection);
    }
    else
    {
        position = ViewSpaceFromScreenSpace(position, gbufferProjectionInverse);
        position = mat3(gbufferModelViewInverse) * position + gbufferModelViewInverse[3].xyz;
        position = mat3(gbufferPreviousModelView) * position + gbufferPreviousModelView[3].xyz;
        position = ScreenSpaceFromViewSpace(position, gbufferPreviousProjection);
    }

    return position;
}

vec3 clip_aabb(vec3 min_color, vec3 max_color, vec3 history_color)
{
    vec3 p_clip = 0.5 * (max_color + min_color);
    vec3 e_clip = 0.5 * (max_color - min_color);

    vec3 v_clip = history_color - p_clip;
    vec3 v_unit = v_clip / e_clip;
    vec3 a_unit = abs(v_unit);
    float ma_unit = max(a_unit.x, max(a_unit.y, a_unit.z));
    bool history_clipped = ma_unit > 1.0;
    vec3 history = history_clipped ? p_clip + v_clip / ma_unit : history_color;

    return history;
}

void main()
{

    /* DRAWBUFFERS:901*/

    vec3 color = vec3(0.0);
    vec2 uv = texcoord;
    float z = texture(depthtex0, texcoord).x;

    ivec2 texel = ivec2(uv * viewResolution);
    float offcenter_rej = 0.5;
    vec3 position = vec3(uv, MinDepth3x3(texel));
    vec3 reprojectedPosition = Reproject(position);

    vec3 scene = texture(colortex0, texcoord).rgb;
    vec3 current = texture(colortex1, texelSize * texel).rgb;
    // vec3 history = BicubicHermiteTextureSample(reprojectedPosition.xy);

    vec3 history = texture(colortex9, reprojectedPosition.xy).rgb;
    float historyWeight = 0.99;

    if (any(lessThan(reprojectedPosition.xy, vec2(0.0))) || any(greaterThan(reprojectedPosition.xy, vec2(1.0))))
    {
        historyWeight = 0.0;
    }
#ifdef TAA_CLIP
    vec2 pixelCenterDist = 1.0 - abs(2.0 * fract(reprojectedPosition.xy * viewResolution) - 1.0);
    historyWeight *= sqrt(pixelCenterDist.x * pixelCenterDist.y) * offcenter_rej + (1.0 - offcenter_rej);

    // Read the texel values

    ivec2 du = ivec2(1.0, 0.0);
    ivec2 dv = ivec2(0.0, 1.0);

    vec3 ctl = texelFetch(colortex1, (texel - dv - du), 0).rgb;
    vec3 ctc = texelFetch(colortex1, (texel - dv), 0).rgb;
    vec3 ctr = texelFetch(colortex1, (texel - dv + du), 0).rgb;
    vec3 cml = texelFetch(colortex1, (texel - du), 0).rgb;
    vec3 cmc = texelFetch(colortex1, (texel), 0).rgb;
    vec3 cmr = texelFetch(colortex1, (texel + du), 0).rgb;
    vec3 cbl = texelFetch(colortex1, (texel + dv - du), 0).rgb;
    vec3 cbc = texelFetch(colortex1, (texel + dv), 0).rgb;
    vec3 cbr = texelFetch(colortex1, (texel + dv + du), 0).rgb;
    vec3 minRounded = min(ctl, min(ctc, min(ctr, min(cml, min(cmc, min(cmr, min(cbl, min(cbc, cbr))))))));
    vec3 maxRounded = max(ctl, max(ctc, max(ctr, max(cml, max(cmc, max(cmr, max(cbl, max(cbc, cbr))))))));

    history = clip_aabb(minRounded, maxRounded, history).rgb;

#endif
    color = ((mix(current, history, historyWeight)));
#endif
    color = clamp(color, 0.0001, 1000.0);

    gl_FragData[0].rgb = color;
    gl_FragData[1].rgb = scene + (color * 1.0);
    gl_FragData[2] = vec4(0);
}
