#version 330 core

// Interpolated values from the vertex shaders
in vec2 UV;
uniform sampler2D inputtex;
uniform sampler2D fftTex;
uniform float fftChannel;
uniform float fftCoef;
uniform float fftScale;
uniform float fftMin;
uniform float iGlobalTime;
uniform float distR;
uniform float distSeed;
uniform float vhsConst;

vec3 mod289(vec3 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec2 mod289(vec2 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec3 permute(vec3 x) {
  return mod289(((x*34.0)+1.0)*x);
}

float snoise(vec2 v) {
  const vec4 C = vec4( 0.211324865405187,  // (3.0-sqrt(3.0))/6.0
             0.366025403784439,  // 0.5*(sqrt(3.0)-1.0)
            -0.577350269189626,  // -1.0 + 2.0 * C.x
             0.024390243902439); // 1.0 / 41.0
  vec2 i  = floor(v + dot(v, C.yy) );
  vec2 x0 = v -   i + dot(i, C.xx);
  vec2 i1;
  i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
  vec4 x12 = x0.xyxy + C.xxzz;
  x12.xy -= i1;
  i = mod289(i);
  vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) + i.x + vec3(0.0, i1.x, 1.0 ));
  vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
  m = m*m ;
  m = m*m ;
  vec3 x = 2.0 * fract(p * C.www) - 1.0;
  vec3 h = abs(x) - 0.5;
  vec3 ox = floor(x + 0.5);
  vec3 a0 = x - ox;
  m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
  vec3 g;
  g.x  = a0.x  * x0.x  + h.x  * x0.y;
  g.yz = a0.yz * x12.xz + h.yz * x12.yw;
  return 130.0 * dot(m, g);
}

float rand(vec2 co) {
  return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

vec4 colorgrade(vec4 color) {
  vec4 new = vec4(0.0);
  new.x = 1.2*pow(color.x, 1.75);
  new.y = pow(color.y+0.15, 0.75);
  new.z = pow(1.1*color.z+0.05, 1.3)+0.05;

  new += vec4(0.4);
  new /= 1.5;
  new.y -= 0.12;
  new.y = pow(new.y, 1.25);
  new = 1.2*pow(new, vec4(2.25));
  return 0.88*vec4(1.46*pow(new.y, 1.2), 1.08*pow(new.x, 0.94)-0.02, 0.9*new.z, new.w);
}

float hash(float f) {
  return fract(sin(f * 11.1753) * 192652.37862);
}

float noise(vec3 x) {
    vec3 p = floor(x);
    vec3 f = fract(x);
    f       = f*f*(3.0-2.0*f);
    float n = p.x + p.y*57.0 + 113.0*p.z;
    return mix(mix(mix( hash(n+0.0),   hash(n+1.0),  f.x),
                   mix( hash(n+57.0),  hash(n+58.0), f.x),f.y),
               mix(mix( hash(n+113.0), hash(n+114.0),f.x),
                   mix( hash(n+170.0), hash(n+171.0),f.x),f.y),f.z);
}

vec4 readItex(vec2 uv) {
  float ss = floor(7.5 * uv.y);
  float rndstuff = 2.0 * (noise(vec3(ss, ss, distSeed)) - 0.5);
  return texture(inputtex, uv + vec2(distR * rndstuff, 0.0));
}

vec4 vhs(vec2 uv, sampler2D inputtex) {
  float magnitude = 0.0009+0.0004*sin(iGlobalTime*1.231);
  vec3 sc = vec3(0.0);
    
  float b = smoothstep(0.0, 1.0, iGlobalTime/100.0);
    
  uv.y -= 0.05*smoothstep(0.85, 0.9, noise( vec3(iGlobalTime-uv.y*2.0, iGlobalTime, iGlobalTime)));
  //uv.x -= 1.0+0.1*smoothstep(0.8, 1.0, noise( vec3(iGlobalTime-sin(uv.y*6.0), iGlobalTime, iGlobalTime)));
    
  // Fucked up tape
  float flop = smoothstep(-0.75, 2.0, sin(0.15/noise(vec3(iGlobalTime*4.0))-1.0));
  float flip = smoothstep(-1.2, 1.5, sin(0.15/noise(vec3(iGlobalTime*3.7))-1.0));
  uv.y -= 0.06*flip-0.011;//pow(atan( 1.5+sin(iGlobalTime) ), 3.0);
  uv.x += 0.11*flop*pow(1.0/(1.5+uv.y), 2.0);
    
  // Loop a few passes for smoothness
  for(int i = 0; i < vhsConst; i++){
    // Distortion magic via sampling offsets
    vec2 ruv = uv;
    ruv.x = uv.x - atan(( 1.0+sin(iGlobalTime*2.3), uv.y))*magnitude*1.25;
        ruv.x += 0.5*tan(rand(vec2(iGlobalTime*0.009,   uv.y )))*magnitude;

    vec2 guv = uv;
    guv.x  = uv.x - sin( tan( 0.2*(uv.y-1.0)*(uv.y-1.0)+iGlobalTime*2.1) )*magnitude*1.6;
    guv.x += 0.003*rand( vec2( tan( 0.01*uv.y+iGlobalTime ) ) );
    guv.x -= cos( tan( 0.2*(uv.y+1.0)*(uv.y+1.0)+iGlobalTime*0.9) )*magnitude*1.0;

    vec2 buv = uv;
        sc.r += 0.94*(1.55/floor(vhsConst))*log(1.0+readItex(ruv).r);
        sc.g += 0.94*(1.19/floor(vhsConst))*readItex(guv ).g;
        sc.b += 0.94*(1.08/floor(vhsConst))*sqrt(readItex(buv ).b-0.1);
 
    magnitude *= 1.25;
    uv *= 1.0015;
    uv -= 0.0006;
  }
    
  // More VHS color "correction" and playback noise
  sc = 0.12+0.85*sc + 0.08*noise( vec3(uv*321.7, 6853.1*(150.0+tan(iGlobalTime))+rand(uv*211.0)) ) ;
  float v  = 1.0 / pow(1.0 + 0.03*dot(uv, uv), 3.0); 
  float tv = 0.99+0.01*sin(100.0*iGlobalTime);
  return vec4(sc*v*tv, 0.0);
}

// output
layout(location = 0) out vec4 color;

void main() {
  if( UV.y < 0.1222 || UV.y > 0.8777) {
    color = vec4(vec3(0.0), 1.0);
  } else {
    vec4 op = vhs(UV, inputtex);
    op = 1.1*colorgrade(0.9*pow(op, vec4(0.76)) );
    
    op += vec4( 0.015*vec3(rand(UV * vec2(iGlobalTime) )), 1.0 );
    op += vec4( 0.03*vec3(snoise(UV*400.0 + vec2(iGlobalTime*1000.0) )), 1.0 );
    if (fftScale >= 0.0) {
      color = vec4(op.rgb, dot(op.rgb, vec3(0.2126, 0.7152, 0.0722))) * (fftMin + fftChannel * fftScale);
    } else {
      color = vec4(op.rgb, dot(op.rgb, vec3(0.2126, 0.7152, 0.0722)));
    }
  }
}