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

varying vec2 texcoord;

uniform sampler2D colortex0;
uniform sampler2D colortex1;
uniform sampler2D colortex3;

uniform sampler2D colortex4;
uniform sampler2D depthtex0;
uniform sampler2D depthtex1;

#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));
}
// #define USE_YCOCG
//  https://software.intel.com/en-us/node/503873
vec3 RGB_YCoCg(vec3 c)
{
// Y = R/4 + G/2 + B/4
// Co = R/2 - B/2
// Cg = -R/4 + G/2 - B/4
#ifndef USE_YCOCG
    return c;
#endif
    return vec3(c.x / 4.0 + c.y / 2.0 + c.z / 4.0, c.x / 2.0 - c.z / 2.0, -c.x / 4.0 + c.y / 2.0 - c.z / 4.0);
}

// https://software.intel.com/en-us/node/503873
vec3 YCoCg_RGB(vec3 c)
{
    // R = Y + Co - Cg
    // G = Y + Cg
    // B = Y - Co - Cg

#ifndef USE_YCOCG
    return c;
#endif
    return cout(vec3(c.x + c.y - c.z, c.x + c.z, c.x - c.y - c.z));
}

vec4 textureYC(sampler2D tex, vec2 uv)
{
    vec4 c = texture2D(tex, uv);
    return vec4(RGB_YCoCg(c.rgb), c.a);
}
vec4 texelFetchYC(sampler2D tex, ivec2 uv, float numb)
{
    vec4 c = texelFetch(tex, ivec2(uv), 0);
    return vec4(RGB_YCoCg(c.rgb), c.a);
}

vec3 catmull(sampler2D sampler, vec2 coord)
{

    const float sharpness = 0.6;

    vec2 res = vec2(textureSize(sampler, 0));
    vec2 view_pixel_size = 1.0 / res;

    vec2 position = res * coord;
    vec2 center_position = floor(position - 0.5) + 0.5;
    vec2 f = position - center_position;
    vec2 f2 = f * f;
    vec2 f3 = f * f2;

    vec2 w0 = -sharpness * f3 + 2.0 * sharpness * f2 - sharpness * f;
    vec2 w1 = (2.0 - sharpness) * f3 - (3.0 - sharpness) * f2 + 1.0;
    vec2 w2 = -(2.0 - sharpness) * f3 + (3.0 - 2.0 * sharpness) * f2 + sharpness * f;
    vec2 w3 = sharpness * f3 - sharpness * f2;

    vec2 w12 = w1 + w2;
    vec2 tc12 = view_pixel_size * (center_position + w2 / w12);
    vec3 center_color = textureYC(sampler, vec2(tc12.x, tc12.y)).rgb;

    vec2 tc0 = view_pixel_size * (center_position - 1.0);
    vec2 tc3 = view_pixel_size * (center_position + 2.0);

    vec4 color = vec4(textureYC(sampler, vec2(tc12.x, tc0.y)).rgb, 1.0) * (w12.x * w0.y) +
                 vec4(textureYC(sampler, vec2(tc0.x, tc12.y)).rgb, 1.0) * (w0.x * w12.y) +
                 vec4(center_color, 1.0) * (w12.x * w12.y) +
                 vec4(textureYC(sampler, vec2(tc3.x, tc12.y)).rgb, 1.0) * (w3.x * w12.y) +
                 vec4(textureYC(sampler, vec2(tc12.x, tc3.y)).rgb, 1.0) * (w12.x * w3.y);

    return color.rgb / color.a;
}

vec4 fetchHistoryPx(ivec2 pix)
{
    return vec4(texelFetchYC(colortex4, pix, 0).xyz, 1);
}

vec3 CubicHermite(vec3 A, vec3 B, vec3 C, vec3 D, float t)
{
    float t2 = t * t;
    float t3 = t * t * t;
    vec3 a = -A / 2.0 + (3.0 * B) / 2.0 - (3.0 * C) / 2.0 + D / 2.0;
    vec3 b = A - (5.0 * B) / 2.0 + 2.0 * C - D / 2.0;
    vec3 c = -A / 2.0 + C / 2.0;
    vec3 d = B;

    return a * t3 + b * t2 + c * t + d;
}

vec3 BicubicHermiteTextureSample(vec2 P)
{
    vec2 pixel = P * viewResolution.xy + 0.5;

    vec2 frac = fract(pixel);
    ivec2 ipixel = ivec2(pixel) - 1;

    vec3 C00 = fetchHistoryPx(ipixel + ivec2(-1, -1)).rgb;
    vec3 C10 = fetchHistoryPx(ipixel + ivec2(0, -1)).rgb;
    vec3 C20 = fetchHistoryPx(ipixel + ivec2(1, -1)).rgb;
    vec3 C30 = fetchHistoryPx(ipixel + ivec2(2, -1)).rgb;

    vec3 C01 = fetchHistoryPx(ipixel + ivec2(-1, 0)).rgb;
    vec3 C11 = fetchHistoryPx(ipixel + ivec2(0, 0)).rgb;
    vec3 C21 = fetchHistoryPx(ipixel + ivec2(1, 0)).rgb;
    vec3 C31 = fetchHistoryPx(ipixel + ivec2(2, 0)).rgb;

    vec3 C02 = fetchHistoryPx(ipixel + ivec2(-1, 1)).rgb;
    vec3 C12 = fetchHistoryPx(ipixel + ivec2(0, 1)).rgb;
    vec3 C22 = fetchHistoryPx(ipixel + ivec2(1, 1)).rgb;
    vec3 C32 = fetchHistoryPx(ipixel + ivec2(2, 1)).rgb;

    vec3 C03 = fetchHistoryPx(ipixel + ivec2(-1, 2)).rgb;
    vec3 C13 = fetchHistoryPx(ipixel + ivec2(0, 2)).rgb;
    vec3 C23 = fetchHistoryPx(ipixel + ivec2(1, 2)).rgb;
    vec3 C33 = fetchHistoryPx(ipixel + ivec2(2, 2)).rgb;

    vec3 CP0X = CubicHermite(C00, C10, C20, C30, frac.x);
    vec3 CP1X = CubicHermite(C01, C11, C21, C31, frac.x);
    vec3 CP2X = CubicHermite(C02, C12, C22, C32, frac.x);
    vec3 CP3X = CubicHermite(C03, C13, C23, C33, frac.x);

    return CubicHermite(CP0X, CP1X, CP2X, CP3X, frac.y);
}

float MinDepth3x3(sampler2D tex, vec2 uv)
{
    ivec2 fragCoord = ivec2(gl_FragCoord.xy);
    float depth = texelFetch(tex, 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(tex, fragCoord + ivec2(x, y), 0).x);
            }
        }
    }

    return depth;
}
float MinDepth3x3DH(sampler2D tex, 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)
{
    // Calculate the center point of the axis-aligned bounding box (AABB)
    vec3 p_clip = 0.5 * (max_color + min_color);

    // Calculate the half-size extents of the AABB
    vec3 e_clip = 0.5 * (max_color - min_color);

    // Compute the vector from the center of the AABB to the history color
    vec3 v_clip = history_color - p_clip;

    // Normalize this vector by the extents of the AABB to get the unit vector
    vec3 v_unit = v_clip / e_clip;

    // Compute the absolute value of the unit vector
    vec3 a_unit = abs(v_unit);

    // Find the maximum component of the absolute unit vector
    float ma_unit = max(a_unit.x, max(a_unit.y, a_unit.z));

    // Check if any component of the absolute unit vector is greater than 1.0
    bool history_clipped = ma_unit > 1.0;

    // If the history color is outside the AABB, clip it to the AABB boundary
    // Otherwise, use the original history color
    vec3 history = history_clipped ? p_clip + v_clip / ma_unit : history_color;

    // Return the clipped (or original) history color
    return history;
}
vec3 clip_aabb_simple(vec3 min_color, vec3 max_color, vec3 history_color)
{
    float tolerance = 0.0;

    // Expand the AABB by the tolerance value
    vec3 min_color_tolerant = min_color - tolerance;
    vec3 max_color_tolerant = max_color + tolerance;

    // Clamp the history color to the extended bounds of the AABB
    vec3 clipped_color = clamp(history_color, min_color_tolerant, max_color_tolerant);

    // Return the clipped color
    return clipped_color;
}

float step = 2.0;

float intensity(in vec4 color)
{
    return sqrt((color.x * color.x) + (color.y * color.y) + (color.z * color.z));
}

float sobel(float stepx, float stepy, vec2 center)
{
    // get samples around pixel
    float tleft = intensity(texture(colortex4, center + vec2(-stepx, stepy)));
    float left = intensity(texture(colortex4, center + vec2(-stepx, 0)));
    float bleft = intensity(texture(colortex4, center + vec2(-stepx, -stepy)));
    float top = intensity(texture(colortex4, center + vec2(0, stepy)));
    float bottom = intensity(texture(colortex4, center + vec2(0, -stepy)));
    float tright = intensity(texture(colortex4, center + vec2(stepx, stepy)));
    float right = intensity(texture(colortex4, center + vec2(stepx, 0)));
    float bright = intensity(texture(colortex4, center + vec2(stepx, -stepy)));

    // Sobel masks (see http://en.wikipedia.org/wiki/Sobel_operator)
    //        1 0 -1     -1 -2 -1
    //    X = 2 0 -2  Y = 0  0  0
    //        1 0 -1      1  2  1

    // You could also use Scharr operator:
    //        3 0 -3        3 10   3
    //    X = 10 0 -10  Y = 0  0   0
    //        3 0 -3        -3 -10 -3

    float x = tleft + 2.0 * left + bleft - tright - 2.0 * right - bright;
    float y = -tleft - 2.0 * top - tright + bleft + 2.0 * bottom + bright;
    float color = sqrt((x * x) + (y * y));
    return clamp(color * 10, 0, 1);
}
float sobel2(float stepx, float stepy, vec2 center)
{
    // get samples around pixel
    float tleft = intensity(texture(colortex0, center + vec2(-stepx, stepy)));
    float left = intensity(texture(colortex0, center + vec2(-stepx, 0)));
    float bleft = intensity(texture(colortex0, center + vec2(-stepx, -stepy)));
    float top = intensity(texture(colortex0, center + vec2(0, stepy)));
    float bottom = intensity(texture(colortex0, center + vec2(0, -stepy)));
    float tright = intensity(texture(colortex0, center + vec2(stepx, stepy)));
    float right = intensity(texture(colortex0, center + vec2(stepx, 0)));
    float bright = intensity(texture(colortex0, center + vec2(stepx, -stepy)));

    float x = tleft + 2.0 * left + bleft - tright - 2.0 * right - bright;
    float y = -tleft - 2.0 * top - tright + bleft + 2.0 * bottom + bright;
    float color = sqrt((x * x) + (y * y));
    return clamp(color * 10, 0, 1);
}
void main()
{

    /* DRAWBUFFERS:4*/

#ifdef TAA
    // Initialize color to zero
    vec3 color = vec3(0.0);

    // Get texture coordinates
    vec2 uv = texcoord;

    // Sample the depth texture at the given texture coordinates
    float z = texture(depthtex0, texcoord).x;

    // Sample the alpha value from the color texture
    float alpha = texture(colortex1, texcoord).a;
    float entity = texture(colortex3, texcoord).g;

    // Convert UV coordinates to texel space
    ivec2 texel = ivec2(uv * viewResolution);

    // Off-center rejection parameter for TAA
    float offcenter_rej = TAA_OFFCENTER_REJECTION;

    // Calculate the position using the minimum depth from a 3x3 neighborhood in depthtex0
    vec3 position = vec3(uv, MinDepth3x3(depthtex0, texel));

    // Calculate an alternative position using the minimum depth from a 3x3 neighborhood in depthtex1
    vec3 position2 = vec3(uv, MinDepth3x3(depthtex1, texel));

    // Blend the two positions based on the alpha value, with higher alpha making position2 more influential
    if (alpha > 0.1)
    {
        position = mix(position2, position, pow(alpha, 10));
    }

    // Reproject the position to account for camera movement or changes
    vec3 reprojectedPosition = Reproject(position);

    // Initialize the history weight for TAA
    float historyWeight = TAA_HISTORY_WEIGHT;

#if defined DISTANT_HORIZONS

    if (z >= 1)
    {
        position = vec3(uv, MinDepth3x3DH(dhDepthTex0, texel));
        reprojectedPosition = ReprojectDH(position);
        historyWeight = 0.98;
        offcenter_rej = 0.1;
    }

#endif

    vec3 current = textureYC(colortex0, texelSize * texel).rgb;
    vec3 history = BicubicHermiteTextureSample(reprojectedPosition.xy);

    // vec3 history = textureYC(colortex4, reprojectedPosition.xy).rgb;

    if (any(lessThan(reprojectedPosition.xy, vec2(0.0))) || any(greaterThan(reprojectedPosition.xy, vec2(1.0))))
    {
        historyWeight = 0.0;
    }
#ifdef TAA_CLIP
    // Calculate the distance to the center of the pixel
    vec2 pixelCenterDist = 1.0 - abs(2.0 * fract(reprojectedPosition.xy * viewResolution) - 1.0);

    // Adjust the history weight based on the distance to the pixel center and offcenter_rej
    historyWeight *= sqrt(pixelCenterDist.x * pixelCenterDist.y) * offcenter_rej + (1.0 - offcenter_rej);

    // Define the offsets for neighboring texels
    ivec2 du = ivec2(1, 0);
    ivec2 dv = ivec2(0, 1);

    // Fetch the RGB values of the 3x3 neighborhood texels
    vec3 ctl = texelFetch(colortex0, texel - dv - du, 0).rgb;
    vec3 ctc = texelFetch(colortex0, texel - dv, 0).rgb;
    vec3 ctr = texelFetch(colortex0, texel - dv + du, 0).rgb;
    vec3 cml = texelFetch(colortex0, texel - du, 0).rgb;
    vec3 cmc = texelFetch(colortex0, texel, 0).rgb;
    vec3 cmr = texelFetch(colortex0, texel + du, 0).rgb;
    vec3 cbl = texelFetch(colortex0, texel + dv - du, 0).rgb;
    vec3 cbc = texelFetch(colortex0, texel + dv, 0).rgb;
    vec3 cbr = texelFetch(colortex0, texel + dv + du, 0).rgb;

    // Compute the minimum and maximum RGB values in the 3x3 neighborhood
    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_simple(minRounded, maxRounded, history).rgb;

#endif
#ifdef NO_ENTITY_TAA
    if (entity > 0.1)
    {
        historyWeight = clamp(1 - entity, 0.15, historyWeight);
        // historyWeight = 1 - clamp(distance(history, current) * 1000, 0.5, historyWeight);
    }
#endif

    color = ((mix(current, history, historyWeight)));

#endif

#ifndef TAA
    vec3 color = texture(colortex0, texcoord).rgb;
#endif
    // color = texture(colortex0, texcoord).rgb;
    // color = texture(colortex0, texcoord).rgb;
    // vec3 at_velocity = (gl_ModelViewMatrix * gl_Vertex).xyz - (gl_PreviousModelViewMatrix * gl_PreviousVertex).xyz
    gl_FragData[0].rgb = clamp(color, 0.0001, 1000.0);
    // gl_FragData[1].rgb = vec3(distance(position, position2));
}
