/* BTW: You can use ANY function as the noise/plasma function
 * It just needs to return a float in the range 0.0 - 17.0
 * But shouldn't return 0.0 or 17.0 (they're exclusive bounds).
 * View this shader on shadertoy: https://www.shadertoy.com/view/t3tSRj#
 */

// is highp wasteful for this shader?
#ifdef GL_FRAGMENT_PRECISION_HIGH
  precision highp float;
#else
  precision mediump float;
#endif

uniform float uTime;
uniform vec2 uResolution;

/* ==== Text Colouring  ==== */
#define PHOSPHOR_COL vec4(224./255., 222./255., 244./255., 1.)
#define BG_COL vec4(24./255., 23./255., 30./255., 1.)
/* ======= Text Size ======= */
#define FONT_SIZE vec2(10.,20.)
#define ROWCOLS vec2(80., 24.)
/* === Text Bloom Effect === */
#define WIDTH 1.2
#define HEIGHT 0.7
#define SMOOTH 0.004
/* ====== Smoke Noise ====== */
const int noiseSwirlSteps = 0;
const float noiseSwirlValue = 1.;
const float noiseSwirlStepValue = noiseSwirlValue / float(noiseSwirlSteps);
const float noiseScale = 1.0;
const float noiseTimeScale = 0.1;

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

float quantise(float val, float n) {
    return clamp(floor(val * n), 0.0, n) / n;
}

float roundSquare(vec2 p, vec2 b, float r) {
    return length(max(abs(p)-b,0.0))-r;
}

// standard roundSquare
float stdRS(vec2 uv, float r) {
    return roundSquare(uv - 0.5, vec2(WIDTH, HEIGHT) + r, 0.05);
}

// ===================================================================
// Description : Array and textureless GLSL 2D/3D/4D simplex
//               noise functions.
//      Author : Ian McEwan, Ashima Arts.
//  Maintainer : ijm
//     Lastmod : 20110822 (ijm)
//     License : Copyright (C) 2011 Ashima Arts. All rights reserved.
//               Distributed under the MIT License. See LICENSE file.
//               https://github.com/ashima/webgl-noise
//
vec3 mod289(vec3 x) {
    return x - floor(x * (1.0 / 289.0)) * 289.0;
}

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

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

vec4 taylorInvSqrt(vec4 r) {
    return 1.79284291400159 - 0.85373472095314 * r;
}

float simplex(vec3 v) {
    const vec2  C = vec2(1.0/6.0, 1.0/3.0) ;
    const vec4  D = vec4(0.0, 0.5, 1.0, 2.0);

    // First corner
    vec3 i  = floor(v + dot(v, C.yyy) );
    vec3 x0 =   v - i + dot(i, C.xxx) ;

    // Other corners
    vec3 g = step(x0.yzx, x0.xyz);
    vec3 l = 1.0 - g;
    vec3 i1 = min( g.xyz, l.zxy );
    vec3 i2 = max( g.xyz, l.zxy );

    vec3 x1 = x0 - i1 + C.xxx;
    vec3 x2 = x0 - i2 + C.yyy; 
    vec3 x3 = x0 - D.yyy;      

    // Permutations
    i = mod289(i);
    vec4 p = permute( permute( permute(
             i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
           + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
           + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));

    // Gradients: 7x7 points over a square, mapped onto an octahedron.
    // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
    float n_ = 0.142857142857; // 1.0/7.0
    vec3  ns = n_ * D.wyz - D.xzx;

    vec4 j = p - 49.0 * floor(p * ns.z * ns.z);  //  mod(p,7*7)

    vec4 x_ = floor(j * ns.z);
    vec4 y_ = floor(j - 7.0 * x_ );    // mod(j,N)

    vec4 x = x_ *ns.x + ns.yyyy;
    vec4 y = y_ *ns.x + ns.yyyy;
    vec4 h = 1.0 - abs(x) - abs(y);

    vec4 b0 = vec4( x.xy, y.xy );
    vec4 b1 = vec4( x.zw, y.zw );

    vec4 s0 = floor(b0)*2.0 + 1.0;
    vec4 s1 = floor(b1)*2.0 + 1.0;
    vec4 sh = -step(h, vec4(0.0));

    vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
    vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;

    vec3 p0 = vec3(a0.xy,h.x);
    vec3 p1 = vec3(a0.zw,h.y);
    vec3 p2 = vec3(a1.xy,h.z);
    vec3 p3 = vec3(a1.zw,h.w);

    //Normalise gradients
    vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
    p0 *= norm.x;
    p1 *= norm.y;
    p2 *= norm.z;
    p3 *= norm.w;

    // Mix final noise value
    vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
    m = m * m;
    return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3) ) );
}


// ===================================================================
// VT220 Font Rendering
// Author/Source : https://www.shadertoy.com/view/llSXDV
//
#define l(y,a,b) roundLine(p, vec2(float(a), float(y)), vec2(float(b), float(y)))
float roundLine(vec2 p, vec2 a, vec2 b) {
	b -= a + vec2(1.0,0.);
	p -= a;
    float f = length(p-clamp(dot(p,b)/dot(b,b),0.0,1.0)*b);
	if (uResolution.y < 320.) // attempt to get rid of aliasing on small resolution
		return smoothstep(1.0, 0.9, f);    
    else if (uResolution.y < 720.)
		return smoothstep(0.75, 0.5, f);    
	else
		return smoothstep(1., 0., f);    
}

float vt220Font(vec2 p, float c) {
    if (c < 1.) return 0.;
    if (p.y > 16.) {
        if (c > 2.) return 0.0;
		if (c > 1.) return l(17,1,9);
    }
    if (p.y > 14.) {
		if (c > 16.) return l(15,3,8);
		if (c > 15.) return l(15,1,8);
		if (c > 14.) return l(15,1,3)+ l(15,7,9);
		if (c > 13.) return l(15,2,8);
		if (c > 12.) return l(15,1,9);
		if (c > 11.) return l(15,2,8);
		if (c > 10.) return l(15,1,3)+ l(15,6,8);
		if (c > 9.) return l(15,4,6);
        if (c > 8.) return l(15,2,4)+ l(15,5,7);
		if (c > 7.) return l(15,2,8);
		if (c > 6.) return l(15,2,8);
		if (c > 5.) return l(15,2,8);
		if (c > 4.) return l(15,2,9);
		if (c > 3.) return l(15,1,8);
		if (c > 2.) return l(15,2,9);
    }
    if (p.y > 12.) {
		if (c > 16.) return l(13,2,4)+ l(13,7,9);
		if (c > 15.) return l(13,2,4)+ l(13,7,9);
		if (c > 14.) return l(13,1,3)+ l(13,7,9);
		if (c > 13.) return l(13,1,3)+ l(13,7,9);
		if (c > 12.) return l(13,1,3);
		if (c > 11.) return l(13,4,6);
		if (c > 10.) return l(13,2,4)+ l(13,5,9);
		if (c > 9.) return l(13,2,8);
		if (c > 8.) return l(13,2,4)+ l(13,5,7);
		if (c > 7.) return l(13,1,3)+ l(13,7,9);
		if (c > 6.) return l(13,1,3)+ l(13,7,9);
		if (c > 5.) return l(13,1,3)+ l(13,7,9);
		if (c > 4.) return l(13,1,3)+ l(15,2,9);
		if (c > 3.) return l(13,1,4)+ l(13,7,9);
		if (c > 2.) return l(13,1,3)+ l(13,6,9);
    }
    if (p.y > 10.) {
		if (c > 16.) return l(11,1,3);
		if (c > 15.) return l(11,2,4)+ l(11,7,9);
		if (c > 14.) return l(11,1,9);
		if (c > 13.) return l(11,7,9);
		if (c > 12.) return l(11,2,5);
		if (c > 11.) return l(11,4,6);
		if (c > 10.) return l(11,3,5)+ l(11,6,8);
		if (c > 9.) return l(11,4,6)+ l(11,7,9);
		if (c > 8.) return l(11,1,8);
		if (c > 7.) return l(11,1,3)+ l(11,7,9);
		if (c > 6.) return l(11,1,3)+ l(11,7,9);
		if (c > 5.) return l(11,1,3)+ l(11,7,9);
		if (c > 4.) return l(11,1,3);
		if (c > 3.) return l(11,1,3)+ l(11,7,9);
		if (c > 2.) return l(11,2,9);
    }
    if (p.y > 8.) {
		if (c > 16.) return l(9,1,3);
		if (c > 15.) return l(9,2,8);
		if (c > 14.) return l(9,1,3)+ l(9,7,9);
		if (c > 13.) return l(9,4,8);
		if (c > 12.) return l(9,4,8);
		if (c > 11.) return l(9,4,6);
		if (c > 10.) return l(9,4,6);
		if (c > 9.) return l(9,2,8);
		if (c > 8.) return l(9,2,4)+ l(9,5,7);
		if (c > 7.) return l(9,1,3)+ l(9,7,9);
		if (c > 6.) return l(9,1,3)+ l(9,7,9);
		if (c > 5.) return l(9,1,3)+ l(9,7,9);
		if (c > 4.) return l(9,1,3)+ l(9,7,9);
		if (c > 3.) return l(9,1,4)+ l(9,7,9);
		if (c > 2.) return l(9,7,9);
    }
    if (p.y > 6.) {
		if (c > 16.) return l(7,1,3);
		if (c > 15.) return l(7,2,4)+ l(7,7,9);
		if (c > 14.) return l(7,2,4)+ l(7,6,8);
		if (c > 13.) return l(7,5,7);
		if (c > 12.) return l(7,7,9);
		if (c > 11.) return l(7,2,6);
		if (c > 10.) return l(7,2,4)+ l(7,5,7);
		if (c > 9.) return l(7,1,3)+ l(7,4,6);
		if (c > 8.) return l(7,1,8);
		if (c > 7.) return l(7,2,8);
		if (c > 6.) return l(7,2,8);
		if (c > 5.) return l(7,2,8);
		if (c > 4.) return l(7,2,8);
		if (c > 3.) return l(7,1,8);
		if (c > 2.) return l(7,2,8);
    }
    if (p.y > 4.) {
		if (c > 16.) return l(5,2,4)+ l(5,7,9);
		if (c > 15.) return l(5,2,4)+ l(5,7,9);
		if (c > 14.) return l(5,3,7);
		if (c > 13.) return l(5,6,8);
		if (c > 12.) return l(5,1,3)+ l(5,7,9);
		if (c > 11.) return l(5,3,6);
		if (c > 10.) return l(5,1,5)+ l(5,6,8);
		if (c > 9.) return l(5,2,8);
		if (c > 8.) return l(5,2,4)+ l(5,5,7);
		if (c > 7.) return 0.;
		if (c > 6.) return 0.;
		if (c > 5.) return 0.;
		if (c > 4.) return 0.;
		if (c > 3.) return l(5,1,3);
		if (c > 2.) return 0.;
    }
    if (p.y > 2.) {
		if (c > 16.) return l(3,3,8);
		if (c > 15.) return l(3,1,8);
		if (c > 14.) return l(3,4,6);
		if (c > 13.) return l(3,1,9);
		if (c > 12.) return l(3,2,8);
		if (c > 11.) return l(3,4,6);
		if (c > 10.) return l(3,2,4)+ l(3,7,9);
		if (c > 9.) return l(3,4,6);
		if (c > 8.) return l(3,2,4)+ l(3,5,7);
		if (c > 7.) return l(3,2,4)+ l(3,6,8);
		if (c > 6.) return l(3,1,3)+ l(3,4,7);
		if (c > 5.) return l(3,2,4)+ l(3,6,8);
		if (c > 4.) return 0.;
		if (c > 3.) return l(3,1,3);
		if (c > 2.) return 0.;
    }
    else {
		if (c > 7.) return 0.;
		if (c > 6.) return l(1,2,5)+ l(1,6,8);
    }
    return 0.0;      
}


// ===================================================================
// Noise Generation Algorithms
//     textLines(...) is for simulating the writing of random characters in line formats
//     https://www.shadertoy.com/view/llSXDV (same author as VT220 font rendering)
//
//     smokeNoise(...) is for the main noise generation algorithm (very tunable)
//     https://www.shadertoy.com/view/MsdGWn
//
float textLines(vec2 uvG) {
    float wt = 5. * (uTime + 0.5*sin(uTime*1.4) + 0.2*sin(uTime*2.9)); // wobbly time
    vec2 uvGt = uvG + vec2(0., floor(wt));
    float ll = rand(vec2(uvGt.y, - 1.)) * ROWCOLS.x; // line length
    
    if (uvG.y > ROWCOLS.y - 2.){
        if (ceil(uvG.x) == floor(min(ll, fract(wt)*ROWCOLS.x)))
        	return 2.;
        if (ceil(uvG.x) > floor(min(ll, fract(wt)*ROWCOLS.x)))
        	return 0.;
    }
    if (uvGt.x > 5. && rand(uvGt) < .075)
        return 0.;
    if (max(5., uvGt.x) > ll)
        return 0.;
       
    return rand(uvGt)*15. + 2.;
}

float fbm3(vec3 v) {
    float result = simplex(v);
    result += simplex(v * 2.) / 2.;
    result += simplex(v * 4.) / 4.;
    result /= (1. + 1./2. + 1./4.);
    return result;
}

float fbm5(vec3 v) {
    float result = simplex(v);
    result += simplex(v * 2.) / 2.;
    result += simplex(v * 4.) / 4.;
    result += simplex(v * 8.) / 8.;
    result += simplex(v * 16.) / 16.;
    result /= (1. + 1./2. + 1./4. + 1./8. + 1./16.);
    return result;
}

float smokeNoise(vec3 v) {
    //  make it curl
    for (int i=0; i<noiseSwirlSteps; i++) {
    	v.xy += vec2(fbm3(v), fbm3(vec3(v.xy, v.z + 1000.))) * noiseSwirlStepValue;
    }
    //  normalize
    return fbm5(v) / 2. + 0.5;
}

void main() {       
   	vec2 uv = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y);
   	vec2 uvT = ROWCOLS * FONT_SIZE * uv / uResolution.xy;
   	vec2 uvG = floor(ROWCOLS * uv / uResolution.xy);
    vec2 uvC = gl_FragCoord.xy / uResolution.xy;
        
    vec2 uvNoise = gl_FragCoord.xy / uResolution.xy;
    uvNoise = ceil(uvNoise * ROWCOLS) / ROWCOLS;
    
    float val;
    if (uTime < 2.0)
        val = textLines(uvG);
    else if (uTime < 2.3)
        val = rand(uvG * uTime) * 17.;
    else {
        float noise = smokeNoise(vec3(uvNoise * noiseScale, uTime * noiseTimeScale));
        // Noise is fed through a sigmoid function, then quantised to integer range 0-17
        val = (exp(noise) / 2.71828);  // increase contrast (normalised 0.0 - 1.0)
        val = 1.0 / val;
        val *= 1.0 / (1.0 + exp(-val)) - 0.5; // subtraction value is tunable (range 0.0 - 0.5)
        val *= quantise(val, 17.0); // quantise by 17 then normalise back to 0.0 - 1.0
        val = pow(18.0, val) - 1.0; // TODO: try changing 18.0 to 200.0 and you'll notice some pretty changes :)
    }
      
    gl_FragColor = vt220Font(uvT - uvG * FONT_SIZE, val) * PHOSPHOR_COL + BG_COL;
}

