uniform float time;
uniform vec4 resolution;
uniform sampler2D tex;

uniform float distortion;

float hash12(vec2 p){
	vec3 p3  = fract(vec3(p.xyx) * 0.1031);
	p3 += dot(p3, p3.yzx + 9.0);
	return fract((p3.x + p3.y) * p3.z);
}

float rand(vec2 x){
	vec2 f = fract(x)*fract(x)*(3.0-2.0*fract(x));
	return mix(mix(hash12(floor(x)),hash12(floor(x)+vec2(1,0)),f.x),mix(hash12(floor(x)+vec2(0,1)),hash12(floor(x)+vec2(1)),f.x),f.y);
}

vec4 ntsc(vec2 uv, float artifact){
	float frameTime = time;
    float bg = pow(smoothstep(0.0, 5.0, frameTime), 4.0);
    artifact /= bg;
    
    // random tape misalignment
    const float tape_mangle = 0.8;
    float mt = time*1.0;
    float garble = 0.25*pow(tape_mangle*rand(vec2(mt))+0.5*rand(vec2(mt*2.31)+0.2*rand(vec2(mt*3.83)))/max(0.5,bg),12.0);
    float goff = garble*(bg*rand(100.0*garble+vec2(uv.y)*2.0)+rand(190.0*garble+vec2(uv.y*2.31)*2.0));
    uv.x = mix(uv.x, mix(uv.x, uv.x-0.5*sqrt(uv.x), 0.8*min(1.0, goff)), distortion);
    
    // yuv fuckup
    float val = 0.95+0.1*pow(abs(-1.5+mod(uv.y*resolution.y,3.0))/1.5,0.5);
    
    // hackers fuckup
	float u_vhs  = 1.0*pow(0.015*rand(vec2(frameTime*2.13)), 20.0);
	float u_sync = 0.02*pow(rand(vec2(frameTime*3.0)),6.0/bg);
	
    // transform matrices
	mat3 rgbtoyuv = mat3(0.299, -0.147,  0.615, 0.587, -0.289, -0.515, 0.114, 0.436, -0.100);
	mat3 yuvtorgb = mat3(1.000, 1.000, 1.000, 0.000, -0.395, 2.032, 1.140, -0.581, 0.000);
	
	vec2 uv_q = uv;
	float syncfuck = mod(u_sync * 3.9238472, 1.0);
	float syncfuck2 = syncfuck + 0.2;
	
	uv_q.y += u_sync;
	float shade = 1.05;
	shade -= mix(0.0, garble*0.5*rand(0.04*vec2(uv_q.x*0.1+uv_q.y*50.0+frameTime*20.0))* 0.2, distortion);
	
	vec2 uv_n = uv_q;
	vec3 yuv = vec3(0.0);
    
    float hpos = pow(0.95*rand(vec2(uv.y)*0.4 + vec2(time*14.0)), 40.0);
    yuv += min(1.0, hpos);
    
	float fix = 0.6+garble+artifact*00.5;
	float lumadelay = -0.0;

	if (u_vhs > 0.05){
		fix += sin(frameTime * 5.3) * 0.12;
		fix += sin(frameTime * 9.52) * 0.05;
		fix += sin(frameTime * 30.0) * 0.05;
		fix += rand( vec2(uv_q.y*59.0+frameTime * 30.0) ) * 0.4;
		fix *= 0.5;
	}
	
    // spread the color components (the REAL "magic")
	float tv = 50.0*frameTime;
    float signalf = 10.0-5.0*pow(rand(vec2(time*1.4)), 8.0);
	for (int xint = 10; xint >= 0; xint -= 1){
		float x = float(xint) / signalf;
		float x1 = (x * -0.08) * fix + lumadelay;
		float x2 = (x *  0.06) * fix + lumadelay;

		vec3 mult = (vec3(1.0) - pow(vec3(x), vec3(0.2, 1.0, 1.0))) * 0.2;
		vec2 uv1 = uv_n + vec2(x1,0.0);
		vec2 uv2 = uv_n + vec2(x2,0.0);
		vec2 uv1b = uv_n + vec2(x1, 1.0/486.0);
		vec2 uv2b = uv_n + vec2(x2, 1.0/486.0);
		
		float vn = 0.001*pow(rand(vec2(x+tv+uv2.x*0.35343, x+tv+uv1.y*1.5235123))*0.95, 5.0);
		uv1.x += vn;
		uv2.x -= vn;
		yuv += rgbtoyuv * (texture2D(tex, uv1).rgb) * mult;
		yuv += rgbtoyuv * (texture2D(tex, uv2).rgb) * mult;
		yuv.r += 2.5*vn;
        // jumble colors with tape misalignment
        yuv /= 1.0+goff*goff*0.75+artifact*0.2;
        
	}
	
    // color spectrum funkiness
    yuv *= val;
    // color channel signal problems
    yuv.gb = mix(yuv.gb, mix(1.1*yuv.gb, vec2(0), min(1.0,2.5*pow(u_vhs*20.0,0.01))), distortion);
    
    // color rotation with tape misalignment
    /*yuv.g *= 1.0+10.0*goff*rand(vec2(goff));
    yuv.b *= 1.0+10.0*goff*rand(vec2(1.0-goff));
    yuv.g += 5.0*min(artifact, 0.2)*(-0.25+rand(vec2(uv.y)))/bg;
	yuv.b += 5.0*min(artifact, 0.2)*(-0.25+rand(vec2(1.0-uv.y)))/bg;
    */
    float huelines = 1.0-pow(garble, 0.15);
    yuv.g += huelines*0.12*(-0.5+rand(10.0*vec2(uv.y+time*10.0)));
    yuv.b += huelines*0.12*(-0.5+rand(1.0-10.0*vec2(uv.y+time*10.0)));
	
    // transform back to rgb
	return vec4(yuvtorgb * yuv * shade, 1.0);
}

void main(){
    vec2 uv = gl_FragCoord.xy/resolution.xy;
    //float artifact = texture2D(tex, uv).a;
    gl_FragColor = ntsc(uv, 0.0);
}