/*
    Fast Approx. Anti-Aliasing
    Author: Raven
*/

uniform vec2 agk_resolution;

uniform sampler2D texture0;
varying vec2 uvVarying;

#define FXAA_REDUCE_MIN (1.0 / 128.0)
#define FXAA_REDUCE_MUL (1.0 / 8.0)
#define FXAA_SPAN_MAX 8.0

#define NW 0
#define NE 1
#define SW 2
#define SE 3
#define M  4

vec2 PixelOffset[5];
vec4 RGB[5];

void main()
{
    vec4 color;
    vec2 inverseVP = 1.0 / agk_resolution.xy;

    // Calculate Neighbour UV Coords
    PixelOffset[ 0 ] = (uvVarying + vec2( -inverseVP.x, -inverseVP.y));
    PixelOffset[ 1 ] = (uvVarying + vec2(  inverseVP.x, -inverseVP.y));
    PixelOffset[ 2 ] = (uvVarying + vec2( -inverseVP.x,  inverseVP.y));
    PixelOffset[ 3 ] = (uvVarying + vec2(  inverseVP.x, inverseVP.y));
    PixelOffset[ 4 ] = vec2(uvVarying);

    // Sampling Neightbouring Pixels
    RGB[NW] = texture2D(texture0, PixelOffset[0].xy);
    RGB[NE] = texture2D(texture0, PixelOffset[1].xy);
    RGB[SW] = texture2D(texture0, PixelOffset[2].xy);
    RGB[SE] = texture2D(texture0, PixelOffset[3].xy);
    RGB[M]  = texture2D(texture0, PixelOffset[4].xy);
    color = RGB[M];

    // Set Luma
    const vec3 Luma = vec3(0.299, 0.587, 0.114);

    // Convert from RGB to Luma
    RGB[NW].w = dot(RGB[NW].xyz, Luma);
    RGB[NE].w = dot(RGB[NE].xyz, Luma);
    RGB[SW].w = dot(RGB[SW].xyz, Luma);
    RGB[SE].w = dot(RGB[SE].xyz, Luma);
    RGB[M].w  = dot(RGB[M].xyz,  Luma);

    // Calc Min-Max Luma
    float lumaMin = min(RGB[M].w, min(min(RGB[NW].w, RGB[NE].w), min(RGB[SW].w, RGB[SE].w))) / 2;
    float lumaMax = max(RGB[M].w, max(max(RGB[NW].w, RGB[NE].w), max(RGB[SW].w, RGB[SE].w)));

    vec2 Dir;
    Dir.x = -((RGB[NW].w + RGB[NE].w) - (RGB[SW].w + RGB[SE].w));
    Dir.y =  ((RGB[NW].w + RGB[SW].w) - (RGB[NE].w + RGB[SE].w));

    float DirReduce = max((RGB[NW].w + RGB[NE].w + RGB[SW].w + RGB[SE].w) * 
                            (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);
    
    float DirMin = 1.0 / (min(abs(Dir.x), abs(Dir.y)) + DirReduce);
    Dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2( -FXAA_SPAN_MAX, -FXAA_SPAN_MAX), Dir * DirMin)) * inverseVP;

    vec3 RGBA = 0.5 * (
        texture2D(texture0, uvVarying + Dir * (1.0 / 3.0 - 0.5)).xyz +
        texture2D(texture0, uvVarying + Dir * (2.0 / 3.0 - 0.5)).xyz 
    );
    vec3 RGBB = RGBA * 0.5 + 0.25 * (
        texture2D(texture0, uvVarying + Dir * -0.5).xyz +
        texture2D(texture0, uvVarying + Dir *  0.5).xyz
    );

    float LumaB = dot(RGBB, Luma);
    if((LumaB < lumaMin) || (LumaB > lumaMax))
        color = vec4(RGBA, color.w);
    else
        color = vec4(RGBB, color.w);

    gl_FragColor = color;
}