#version 330 core

in vec2 vUV;
out vec4 FragColor;

uniform vec3 iResolution;
uniform int iFrame;
uniform float uAlpha;
uniform float uStartDelay;
uniform float uDuration;
uniform sampler2D iChannel0;

float hash(vec2 p) {
    return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}

// rotation 2D
mat2 rot(float a) {
    float s = sin(a);
    float c = cos(a);
    return mat2(c, -s, s, c);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
    vec2 uv = fragCoord / iResolution.xy;

    float time = float(iFrame) / 60.0;

    float duration = max(0.001, uDuration);
    float totalTime = uStartDelay + duration;
    float t = min(time, totalTime);

    float p = clamp((t - uStartDelay) / duration, 0.0, 1.0);
    p = pow(p, 3.0);

    float zoom = mix(1.0, 18.0, p);

    vec2 zoomUV = (uv - 0.5) / zoom + 0.5;
    zoomUV = clamp(zoomUV, 0.0, 1.0);

    // impact
    float impact = smoothstep(0.8, 1.0, p);

    vec2 center = vec2(0.5);

    // rotation during impact
    vec2 rUV = zoomUV - center;

    // fast final spin
    float angle = impact * impact * 1.5;

    rUV = rot(angle) * rUV;

    zoomUV = rUV + center;

    // radial distortion
    vec2 dir = zoomUV - center;
    float dist = length(dir);
    zoomUV += dir * dist * impact * 0.3;

    // glitch
    vec2 glitchUV = zoomUV;

    if (impact > 0.0) {
        float strength = impact;

        float lines = step(0.8, hash(vec2(floor(glitchUV.y * 200.0), floor(time * 10.0))));
        glitchUV.x += lines * (hash(vec2(time, glitchUV.y)) - 0.5) * 0.15 * strength;

        glitchUV.y += sin(glitchUV.x * 80.0 + time * 25.0) * 0.02 * strength;

        glitchUV += (vec2(hash(glitchUV + time), hash(glitchUV - time)) - 0.5) * 0.03 * strength;
    }

    float shift = impact * 0.02;

    float r = texture(iChannel0, glitchUV + vec2( shift, 0.0)).r;
    float g = texture(iChannel0, glitchUV).g;
    float b = texture(iChannel0, glitchUV - vec2( shift, 0.0)).b;

    vec3 col = vec3(r, g, b);

    col += impact * 0.4;

    fragColor = vec4(col, 1.0);
}

void main()
{
    mainImage(FragColor, gl_FragCoord.xy);
    FragColor.a *= uAlpha;
}
