#version 450
#pragma shader_stage(fragment)
#extension GL_EXT_samplerless_texture_functions : require

layout (location = 0) in vec2 inUv;
layout (location = 1) flat in uint inPass;

layout (location = 0) out vec4 outColor;

layout (binding = 0) uniform sampler2D	samplerColor;

layout (binding = 1) uniform UBO {
	float curTime;
	float gamma;
	float exposure;
	uint padding;
} ubo;

#define TONE_MAP 1
#define GAMMA_CORRECT 1
#define TEXTURES 1

#include "../../common/macros.h"
#include "../../common/structs.h"
#include "../../common/functions.h"

#define M_PI 3.1415926535897932384626433832795

const float u_imgx = 0;
const float u_imgy = 0;
const float u_imgw = 1;
const float u_imgh = 1;
const float u_img_gain = 2;
const float u_img_bias = 0;
const float u_beam_bias = 0.185;
const float u_beam_gain = 0.25;
const float u_corner = 0.05;
const float u_zoom = 1.0;
const float u_shape = 2;
const float u_round = -0.02;
const float u_grain = 0.4;
const float u_vpitch = 936.1;
const float u_hpitch = 1024.6;
const float u_top = 1;
const float u_bot = 1;

void main() {
	const vec2 screenResolution = textureSize( samplerColor, 0 );
	const float u_lines = screenResolution.y * 0.75;

	vec2 uv_orig = (inUv.xy * 2.0 - 1.0);
	vec2 uv_mod = uv_orig * pow(1.0-abs(uv_orig),vec2(u_round)) * (u_zoom + u_corner * pow( abs(uv_orig.yx), vec2(u_shape)) );
	vec2 uv = uv_mod / 2.0 + 0.5;

	if ( abs(uv_mod).x > 1.0 || abs(uv_mod.y) > 1.0 ) {
		outColor = vec4(0.0, 0.0, 0.0, 1.0);
		return;
	}

	float spacing = 1.0/u_lines;
	uv+=spacing*0.5;

	float line_top = ( ceil(uv.y*u_lines)) / u_lines;
	float line_bot = line_top - spacing;

	vec2 scale = vec2(u_imgw, u_imgh);
	vec2 offset = vec2(u_imgx, u_imgy);

	vec2 uv_top = (vec2(uv.x, line_top)+offset) * scale - (scale-1.0)*0.5 ;
	vec2 uv_bot = (vec2(uv.x, line_bot)+offset) * scale - (scale-1.0)*0.5 ;

	uv_top -= spacing * 0.5;
	uv_bot -= spacing * 0.5;

	vec4 sampled_top = texture(samplerColor, uv_top);
	vec4 sampled_bot = texture(samplerColor, uv_bot);

	vec3 color_top = sampled_top.xyz * u_img_gain + u_img_bias;
	vec3 color_bot = sampled_bot.xyz * u_img_gain + u_img_bias;

	float dist_top = pow(abs(uv.y - line_top), 1.0);
	float dist_bot = pow(abs(uv.y - line_bot), 1.0);

	vec3 beam_top = 1.0 - (dist_top / (spacing * (color_top * u_beam_gain + u_beam_bias)));
	vec3 beam_bot = 1.0 - (dist_bot / (spacing * (color_bot * u_beam_gain + u_beam_bias)));
	
	beam_top = clamp(beam_top, 0.0, 1.0) ;
	beam_bot = clamp(beam_bot, 0.0, 1.0) ;

	vec3 color = (color_top*beam_top)*u_top + (color_bot*beam_bot)*u_bot;

	vec2 dot;
	dot.y = floor(uv.y * u_vpitch) ;
	dot.x = uv.x;

	if (mod(dot.y, 2.0) > 0.5)
		dot.x += (4.5/3.0) / u_hpitch;

	dot.x = (floor(dot.x*u_hpitch)) ;

	int fil = int(mod(dot.x, 3.0));
	
	vec3 out_color = color * (1.0-u_grain);
	
	vec3 passthru = vec3( 
		float(fil == 0), 
		float(fil == 1),
		float(fil == 2)
	) * (u_grain);

	out_color += color * passthru;

	outColor = vec4(out_color, (sampled_top.a + sampled_bot.a) * 0.5 );

#if TONE_MAP
	toneMap(outColor, ubo.exposure);
#endif
#if GAMMA_CORRECT
	gammaCorrect(outColor, ubo.gamma);
#endif
}

#if 0
float iTime = 0;
float noise(vec2 p) {
	float s = (fract(sin(dot(p * sin( iTime * 0.5 ), vec2(12.9898,78.233)*2.0)) * 43758.5453)); // texture(iChannel1,vec2(1.,2.*cos(iTime))*iTime*8. + p*1.).x;
	s *= s;
	return s;
}

float onOff(float a, float b, float c) {
	return step(c, sin(iTime + a*cos(iTime*b)));
}

float ramp(float y, float start, float end) {
	float inside = step(start,y) - step(end,y);
	float fact = (y-start)/(end-start)*inside;
	return (1.-fact) * inside;
	
}

float stripes(vec2 uv) {
	float noi = noise(uv*vec2(0.5,1.) + vec2(1.,3.));
	return ramp(mod(uv.y*4. + iTime/2.+sin(iTime + sin(iTime*0.63)),1.),0.5,0.6)*noi;
}

vec4 getVideo(vec2 uv) {
	vec2 look = uv;
	float window = 1./(1.+20.*(look.y-mod(iTime/4.,1.))*(look.y-mod(iTime/4.,1.)));
	look.x = look.x + sin(look.y*10. + iTime)/50.*onOff(4.,4.,.3)*(1.+cos(iTime*80.))*window;
	float vShift = 0.4*onOff(2.,3.,.9)*(sin(iTime)*sin(iTime*20.) + 
										 (0.5 + 0.1*sin(iTime*200.)*cos(iTime)));
	look.y = mod(look.y + vShift, 1.);
	return texture(samplerColor,look);
}

vec2 screenDistort(vec2 uv) {
	uv -= vec2(.5,.5);
	uv = uv*1.2*(1./1.2+2.*uv.x*uv.x*uv.y*uv.y);
	uv += vec2(.5,.5);
	return uv;
}

void main() {
	vec2 uv = inUv.xy; // fragCoord.xy / iResolution.xy;
	iTime = ubo.curTime;

	uv = screenDistort(uv);
	vec4 video = getVideo(uv);
	float vigAmt = 3.+.3*sin(iTime + 5.*cos(iTime*5.));
	float vignette = (1.-vigAmt*(uv.y-.5)*(uv.y-.5))*(1.-vigAmt*(uv.x-.5)*(uv.x-.5));
	
	video.rgb += stripes(uv);
	video.rgb += noise(uv*2.)/4.;
	video.rgb *= vignette;
	video.rgb *= (12.+mod(uv.y*30.+iTime,1.))/13.;
	
	outColor = video;
#if TONE_MAP
	toneMap(outColor, ubo.exposure);
#endif
#if GAMMA_CORRECT
	gammaCorrect(outColor, ubo.gamma);
#endif
}
#endif
#if 0
vec2 curveRemapUV(vec2 uv, vec2 curvature) {
	uv = uv * 2.0 - 1.0;
	vec2 offset = abs(uv.yx) / vec2(curvature.x, curvature.y);
	uv = uv + uv * offset * offset;
	uv = uv * 0.5 + 0.5;
	return uv;
}

vec4 scanLineIntensity(float uv, float resolution, float opacity) {
	float intensity = sin(uv * resolution * PI * 2.0);
	intensity = ((0.5 * intensity) + 0.5) * 0.9 + 0.1;
	return vec4(vec3(pow(intensity, opacity)), 1.0);
}

vec4 vignetteIntensity(vec2 uv, vec2 resolution, float opacity, float roundness) {
	float intensity = uv.x * uv.y * (1.0 - uv.x) * (1.0 - uv.y);
	return vec4(vec3(clamp(pow((resolution.x / roundness) * intensity, opacity), 0.0, 1.0)), 1.0);
}

void main(void) {
	const vec2 screenResolution = textureSize( samplerColor, 0 );
	const vec2 scanLineOpacity = vec2(0.5);
	const float brightness = 2;
	const float vignetteOpacity = 0.5;
	const float vignetteRoundness = 0.5;
	const vec2 curvature = vec2(8);

	const vec2 remappedUV = curveRemapUV(vec2(inUv.x, inUv.y), curvature);
	vec4 baseColor = texture(samplerColor, remappedUV);

	baseColor *= vignetteIntensity(remappedUV, screenResolution, vignetteOpacity, vignetteRoundness);

	baseColor *= scanLineIntensity(remappedUV.x, screenResolution.y, scanLineOpacity.x);
	baseColor *= scanLineIntensity(remappedUV.y, screenResolution.x, scanLineOpacity.y);

	baseColor *= vec4(vec3(brightness), 1.0);

	if (remappedUV.x < 0.0 || remappedUV.y < 0.0 || remappedUV.x > 1.0 || remappedUV.y > 1.0){
		outColor = vec4(0.0, 0.0, 0.0, 1.0);
	} else {
		outColor = baseColor;
	}

#if TONE_MAP
	toneMap(outColor, ubo.exposure);
#endif
#if GAMMA_CORRECT
	gammaCorrect(outColor, ubo.gamma);
#endif
}
#endif
#if 0
vec2 fragCoord = vec2(0,0);
vec2 iResolution = vec2(640,480);

// Emulated input resolution.
#if 0
	// Fix resolution to set amount.
	#define res (vec2(320.0/1.0,160.0/1.0))
#else
	// Optimize for resize.
	#define res (iResolution.xy/6.0)
#endif

// Hardness of scanline.
//	-8.0 = soft
// -16.0 = medium
float hardScan=-8.0;

// Hardness of pixels in scanline.
// -2.0 = soft
// -4.0 = hard
float hardPix=-3.0;

// Display warp.
// 0.0 = none
// 1.0/8.0 = extreme
vec2 warp=vec2(1.0/32.0,1.0/24.0); 

// Amount of shadow mask.
float maskDark=0.5;
float maskLight=1.5;

//------------------------------------------------------------------------

// sRGB to Linear.
// Assuing using sRGB typed textures this should not be needed.
float ToLinear1(float c){return(c<=0.04045)?c/12.92:pow((c+0.055)/1.055,2.4);}
vec3 ToLinear(vec3 c){return vec3(ToLinear1(c.r),ToLinear1(c.g),ToLinear1(c.b));}

// Linear to sRGB.
// Assuing using sRGB typed textures this should not be needed.
float ToSrgb1(float c){return(c<0.0031308?c*12.92:1.055*pow(c,0.41666)-0.055);}
vec3 ToSrgb(vec3 c){return vec3(ToSrgb1(c.r),ToSrgb1(c.g),ToSrgb1(c.b));}

// Nearest emulated sample given floating point position and texel offset.
// Also zero's off screen.
vec3 Fetch(vec2 pos,vec2 off){
	pos=floor(pos*res+off)/res;
	if(max(abs(pos.x-0.5),abs(pos.y-0.5))>0.5)return vec3(0.0,0.0,0.0);
	return ToLinear(texture(samplerColor,pos.xy,-16.0).rgb);}

// Distance in emulated pixels to nearest texel.
vec2 Dist(vec2 pos){pos=pos*res;return -((pos-floor(pos))-vec2(0.5));}
	
// 1D Gaussian.
float Gaus(float pos,float scale){return exp2(scale*pos*pos);}

// 3-tap Gaussian filter along horz line.
vec3 Horz3(vec2 pos,float off){
	vec3 b=Fetch(pos,vec2(-1.0,off));
	vec3 c=Fetch(pos,vec2( 0.0,off));
	vec3 d=Fetch(pos,vec2( 1.0,off));
	float dst=Dist(pos).x;
	// Convert distance to weight.
	float scale=hardPix;
	float wb=Gaus(dst-1.0,scale);
	float wc=Gaus(dst+0.0,scale);
	float wd=Gaus(dst+1.0,scale);
	// Return filtered sample.
	return (b*wb+c*wc+d*wd)/(wb+wc+wd);}

// 5-tap Gaussian filter along horz line.
vec3 Horz5(vec2 pos,float off){
	vec3 a=Fetch(pos,vec2(-2.0,off));
	vec3 b=Fetch(pos,vec2(-1.0,off));
	vec3 c=Fetch(pos,vec2( 0.0,off));
	vec3 d=Fetch(pos,vec2( 1.0,off));
	vec3 e=Fetch(pos,vec2( 2.0,off));
	float dst=Dist(pos).x;
	// Convert distance to weight.
	float scale=hardPix;
	float wa=Gaus(dst-2.0,scale);
	float wb=Gaus(dst-1.0,scale);
	float wc=Gaus(dst+0.0,scale);
	float wd=Gaus(dst+1.0,scale);
	float we=Gaus(dst+2.0,scale);
	// Return filtered sample.
	return (a*wa+b*wb+c*wc+d*wd+e*we)/(wa+wb+wc+wd+we);}

// Return scanline weight.
float Scan(vec2 pos,float off){
	float dst=Dist(pos).y;
	return Gaus(dst+off,hardScan);}

// Allow nearest three lines to effect pixel.
vec3 Tri(vec2 pos){
	vec3 a=Horz3(pos,-1.0);
	vec3 b=Horz5(pos, 0.0);
	vec3 c=Horz3(pos, 1.0);
	float wa=Scan(pos,-1.0);
	float wb=Scan(pos, 0.0);
	float wc=Scan(pos, 1.0);
	return a*wa+b*wb+c*wc;}

// Distortion of scanlines, and end of screen alpha.
vec2 Warp(vec2 pos){
	pos=pos*2.0-1.0;		
	pos*=vec2(1.0+(pos.y*pos.y)*warp.x,1.0+(pos.x*pos.x)*warp.y);
	return pos*0.5+0.5;}

// Shadow mask.
vec3 Mask(vec2 pos){
	pos.x+=pos.y*3.0;
	vec3 mask=vec3(maskDark,maskDark,maskDark);
	pos.x=fract(pos.x/6.0);
	if(pos.x<0.333)mask.r=maskLight;
	else if(pos.x<0.666)mask.g=maskLight;
	else mask.b=maskLight;
	return mask;}		

void main() {
	iResolution = textureSize( samplerColor, 0 );
	fragCoord = inUv * iResolution;

	vec2 pos = Warp(fragCoord.xy / iResolution.xy);
	
		hardScan = -12.0;
		maskDark = maskLight = 1.0;

	outColor.rgb = Tri(pos) * Mask(fragCoord.xy);
	outColor.rgb = ToSrgb(outColor.rgb);
	outColor.a = 1.0;

#if TONE_MAP
	toneMap(outColor, ubo.exposure);
#endif
#if GAMMA_CORRECT
	gammaCorrect(outColor, ubo.gamma);
#endif
}
#endif
#if 0
void main() {
	const vec2 uv = 0.025 * sin( ubo.curTime ) * inUv.xy;
	const float mdf = 0.5;
	const float noise = (fract(sin(dot(uv, vec2(12.9898,78.233)*2.0)) * 43758.5453));
	const vec4 sampled = texture( samplerColor, inUv );
	
	outColor = sampled - noise * mdf;

	toneMap(outColor, ubo.exposure);
	gammaCorrect(outColor, ubo.gamma);
}
#endif