#version 450
#include "config.inc"

#include "includes/functions.include.slang" 

#pragma stage vertex
layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord;
layout(location = 0) noperspective out vec2 vTexCoord;
layout(location = 2) flat out float vGlow_w_needed;
layout(location = 3) flat out float vSharp_sub;
layout(location = 4) flat out float vDo_wtfl;
layout(location = 5) flat out float vWtfl_sharpness_x;
layout(location = 12) flat out float vWarpedglow_scale_tated;
layout(location = 14) flat out float vIn_glow_edge_sharpness;
layout(location = 15) flat out float vIn_glow_edge_defocus_strength;
layout(location = 16) noperspective out float vWarpedglow_vtexcoord_offsets_plus;
layout(location = 17) noperspective out float vWarpedglow_vtexcoord_offsets_minus;

void main() {
    gl_Position = global.MVP * Position;
    vTexCoord = TexCoord ;  
    
    bool bDo_wtfl = (DO_WTFL > 0.9 && WTFL_SHARPNESS_X < 3.0);
    bool bDo_defocus = (DO_SHIFT_RGB > 0.5 && EDGE_DEFOCUS_DO_BLUR > 0.0);
    
    vDo_wtfl = float(bDo_wtfl);
    
    //The following conditions are used to skip blur-x code.
    bool bGlow_needed = true;
    if (IN_GLOW_W >= GLOW_SHARP_MAX) bGlow_needed = false;                 // radius falls in the allowed interval
    if (DO_IN_GLOW < 0.5) bGlow_needed = false;                              // Disabled if not user enabled
    if (bDo_wtfl) bGlow_needed = true;     //always active if need to blur the waterfalls.
    if (bDo_defocus) bGlow_needed = true; //always active if need to do defocusing.
    
    vGlow_w_needed = float( bGlow_needed);


    //if (IN_GLOW_W < 0.0) {
        //Compute subtractive sharpening via blur sharpness strength:
        //For sharpness 0.45, no unsharp mask
        //For sharpness 0.8, 0.65 unsharp mask
        //float sharp_sub = (1.857 * abs(IN_GLOW_W) - 0.836);
        float in_glow_w = abs(IN_GLOW_W);
        in_glow_w = max(in_glow_w, MIN_IN_GLOW_SHARP);
        vSharp_sub = 0.8125 * in_glow_w;
        vSharp_sub = clamp(vSharp_sub, 0.0,1.0);
        vSharp_sub = -2*vSharp_sub+1;
    //}
        
    
    float warpedglow_delta = params.OriginalSize.z * 0.5;
        
    vWarpedglow_vtexcoord_offsets_plus = vTexCoord.x  + warpedglow_delta;
    vWarpedglow_vtexcoord_offsets_minus = vTexCoord.x - warpedglow_delta;
    
    vWarpedglow_scale_tated = abs(IN_GLOW_WARPSHARP_X);
    
    //Disable warpdglow x if user requested scanline modulation on particular conditions like:
    //fake integer scanlines and interlacing (leave it on on real interlacing)
    vWarpedglow_scale_tated *= warpedglow_x_allowed(IN_GLOW_WARPSHARP_X);
    
    if (need_tate(is_rotated()) == 1.0)
        vWarpedglow_scale_tated = abs(IN_GLOW_WARPSHARP_Y);
    
    vWarpedglow_scale_tated = vWarpedglow_scale_tated * 0.6 / 1400.0 * 3.0;

    vWarpedglow_scale_tated /= get_in_aspect() ; //correct aspect
    
    if (EDGE_DEFOCUS_DO_BLUR*DO_SHIFT_RGB > 0.001) {
        vIn_glow_edge_defocus_strength = EDGE_DEFOCUS_DO_BLUR;
    } else {
        vIn_glow_edge_defocus_strength = 0.0;
    }
    vIn_glow_edge_sharpness = mix(abs(in_glow_w), 0.3, vIn_glow_edge_defocus_strength);
    //vIn_glow_edge_sharpness = max(vIn_glow_edge_sharpness, MIN_IN_GLOW_SHARP);

    vWtfl_sharpness_x = (WTFL_SHARPNESS_X+1) * NEW_SCALEMOD_X;
    if (IN_GLOW_W < 0.0) vWtfl_sharpness_x /= 1.2;


}


#pragma stage fragment

layout(location = 0) noperspective in vec2 vTexCoord;
layout(location = 2) flat in float vGlow_w_needed;
layout(location = 3) flat in float vSharp_sub;
layout(location = 4) flat in float vDo_wtfl;
layout(location = 5) flat in float vWtfl_sharpness_x;
layout(location = 12) flat in float vWarpedglow_scale_tated;

layout(location = 14) flat in float vIn_glow_edge_sharpness;
layout(location = 15) flat in float vIn_glow_edge_defocus_strength;
layout(location = 16) noperspective in float vWarpedglow_vtexcoord_offsets_plus;
layout(location = 17) noperspective in float vWarpedglow_vtexcoord_offsets_minus;


layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 5) uniform sampler2D colortools_and_ntsc_pass;
layout(set = 0, binding = 6) uniform sampler2D colortools_and_ntsc_passFeedback;
layout(set = 0, binding = 7) uniform sampler2D shift_and_bleed_pass;


vec3 blur_select_wrap(sampler2D texture_in, vec2 co, vec4 sourcesize, float sharpness, float sharp_sub, bool use_gauss) {
    if (use_gauss)
        return blur9_x(texture_in, co, sourcesize.xy, sharpness);
            else
        return blur9_x_box(texture_in, co, sourcesize.xy, sharpness, sharp_sub);
}


vec3 unsharp_compensation(vec3 c, vec2 co, float amount) {
    float dx = params.OriginalSize.z;
    //vec3 c = texture(shift_and_bleed_pass, vTexCoord).rgb;
    vec3 cl =texture(shift_and_bleed_pass, co + vec2(dx, 0.0)).rgb ;
    vec3 cr =texture(shift_and_bleed_pass, co - vec2(dx, 0.0)).rgb ;
    vec3 blur = ( c + cl + cr ) / 3.0;
    vec3 sharped = c + (c - blur) * amount;
    return clamp(sharped, 0.0,1.0);
}

void main() { 
    
    #ifndef D3D_WORKAROUND
        if (is_skipped_frame())
            return;
    #endif
    
    //Warped Glow X
    //The result will be stored in the alpha channel to be consumed by final pass.
    float warpedglow_x = 0.0;
    #ifndef SKIP_WARPEDGLOW
        if (DO_IN_GLOW == 1.0) {
            float max_inLum = max( 1.0, DO_CCORRECTION * apply_contrast_brightness(1.0, CONTRAST, BRIGHTNESS));
            vec3 gx = texture(shift_and_bleed_pass, vec2(vWarpedglow_vtexcoord_offsets_plus, vTexCoord.y)).rgb
                    - texture(shift_and_bleed_pass, vec2(vWarpedglow_vtexcoord_offsets_minus, vTexCoord.y)).rgb;
                
            gx /=max_inLum;

            warpedglow_x = (gx.x+gx.y+gx.z) / 3.0 ;
            warpedglow_x = pow( abs(warpedglow_x), IN_GLOW_WARPSHARP_GAMMA) * sign(warpedglow_x);
            warpedglow_x =  warpedglow_x * vWarpedglow_scale_tated;
        }
    #endif        
    
    //Don't do anything if IN_GLOW_H is are over the upper limit
    //and IN_GLOW_W is outside the allowed interval;
        if ( vGlow_w_needed == 0.0) {
            //passthrough, almost, apply unsharp if requested.
            vec3 pixel_out_early = texture(shift_and_bleed_pass, vTexCoord).rgb;
            if ( CVBS_UNSHARP_COMPENSATION * DO_CVBS_BLEED  > 0.0)
                pixel_out_early = unsharp_compensation(pixel_out_early, vTexCoord, CVBS_UNSHARP_COMPENSATION);
            FragColor =vec4(pixel_out_early, warpedglow_x);
            return;
        }
        
    float in_glow_w_adapted = abs(IN_GLOW_W);
    float sharp_sub_adapted = vSharp_sub;
    
    float waterfall = 0.0;
    //Override Megadrive fake transparencies (like Sonic waterfalls)
        if (vDo_wtfl == 1.0) {
            waterfall = texture(colortools_and_ntsc_pass, vTexCoord).a; //somehow there is no need to use texture_nearest here.
            waterfall = unpack_21(waterfall, 1.2).x; //FragColor = vec4(vec3(waterfall),0.0);return;

            if (DO_IN_GLOW == 0.0)
                in_glow_w_adapted = 7.0;
            in_glow_w_adapted = mix(in_glow_w_adapted, vWtfl_sharpness_x, waterfall);
            //cancel sharp sub when doing fake transparencies blur.
            sharp_sub_adapted = mix(sharp_sub_adapted, 1.0, waterfall );
        }
    
    //Scale it for box (waterfall blending counteracts this scaling, consider that).
        if (IN_GLOW_W < 0.0)
            in_glow_w_adapted = in_glow_w_adapted/1.2;
        
    // Edge defocus:
        float dist_from_edge = 1.0;
        if (vIn_glow_edge_defocus_strength > 0.0) {
            dist_from_edge = distance_from_edge_linear(vTexCoord, EDGE_DEFOCUS_AREA_INV);
            
            //never decrease blur:
                float new_in_glow_w_adapted = mix(vIn_glow_edge_sharpness, in_glow_w_adapted,  dist_from_edge );
                in_glow_w_adapted = min(new_in_glow_w_adapted, in_glow_w_adapted);

            //cancel sharp sub when doing defocus blur
            sharp_sub_adapted = mix(sharp_sub_adapted, 1.0, (1-dist_from_edge)*vIn_glow_edge_defocus_strength);
            //FragColor = vec4(vec3(dist_from_edge), 0.0); return;
        }
    //Make it resolution independent.
        in_glow_w_adapted = in_glow_w_adapted / NEW_SCALEMOD_X;
        

    //Also, a little hacky adaption for when upscale_pass does not scale to 2X
        if (params.OutputSize.xy == params.OriginalSize.xy)
            in_glow_w_adapted*=1.5;
            
    vec3 pixel_out = blur_select_wrap(shift_and_bleed_pass, vTexCoord, global.upscale_passSize, in_glow_w_adapted, sharp_sub_adapted, (IN_GLOW_W >= 0.0));

    if ( CVBS_UNSHARP_COMPENSATION * DO_CVBS_BLEED  > 0.0 ) {
        vec3 pixel_out_unsharp = unsharp_compensation(pixel_out,vTexCoord,CVBS_UNSHARP_COMPENSATION);
        float bypass_unsharp = max(waterfall, (1-dist_from_edge)*vIn_glow_edge_defocus_strength);
        pixel_out = mix(pixel_out_unsharp, pixel_out, bypass_unsharp);
    }
    FragColor = vec4(pixel_out, warpedglow_x);
}

