// Glitch family: tape. The transport is not quite steady, so every line of the
// image is displaced by where the tape was when that line was written — a
// continuous horizontal warp, chroma trailing behind luma, and the occasional
// dropout where the oxide has worn through.
//
// Scan Tear and Block Mosh are digital faults: they are quantised, blocky, and
// they happen to whole regions at once. This is analogue and wet — nothing has
// an edge, the error varies smoothly down the frame, and the colour lags the
// picture instead of being replaced by it. Wow and flutter, not corruption.
//
// No declared slow axis: the dropouts and the head smear put two windows of
// identical parameters 0.041 apart, the second-highest noise floor in the
// library, and chroma bleed against that measured 0.02x.

export const analogWow = {
    name: 'Analog Wow',
    family: 'glitch',
    kind: 'fragment',
    texture: 0.9,
    consumes: ['ink'],
    traits: ['camera', 'style'],

    params: {
        wow:      { type: 'float', range: [0, 0.12],    default: 0.035, uniform: 'u_wow' },
        flutter:  { type: 'float', range: [0, 0.03],    default: 0.008, uniform: 'u_flutter' },
        bleed:    { type: 'float', range: [0, 0.05],    default: 0.015, uniform: 'u_bleed' },
        dropout:  { type: 'float', range: [0, 0.6],     default: 0.2,   uniform: 'u_dropout' },
        bars:     { type: 'float', range: [1, 9],       default: 3.5,   uniform: 'u_bars', bias: 'density' },
        speed:    { type: 'float', range: [0.05, 0.7],  default: 0.2,   uniform: 'u_speed', bias: 'motion', rate: true },
        palette:  { type: 'palette', count: 5 },
    },

    reactive: {
        dropout: { feature: 'flux',     amount: 0.3,  response: 'spike' },
        wow:     { feature: 'bandLow',  amount: 0.25, response: 'smooth' },
    },

    shader: `
// What was recorded: broad soft bars of colour, the kind of thing that shows
// tape damage clearly because it has no detail of its own to hide it behind.
vec3 programme(vec2 q, float t) {
    float band = q.y * u_bars + fbm(q * 1.4 + vec2(t * 0.3, 0.0), 3) * 1.2;
    vec3 col = palRamp(0.1 + band * 0.12);
    col *= 0.55 + 0.45 * sat(0.5 + 0.5 * sin(band * 3.14159 + t));
    col += pal(4) * smoothstep(0.85, 1.0, sat(0.5 + 0.5 * sin(q.x * 3.0 - t * 1.3))) * 0.25;
    // The boundary between bands is drawn in the track's weight, so even a
    // damaged signal is damaged in the video's own hand.
    col += pal(4) * inkStroke(fract(band) - 0.5) * (0.15 + u_sigLine * 0.9);
    return col * 0.7;
}

vec4 scene(vec2 uv, vec2 p) {
    float t = u_time * u_speed + u_seed;
    p = sigCamera(p);

    // Transport error for THIS line: a slow wow, a faster flutter, and a little
    // noise on top. It is a function of y, which is what makes the frame skew
    // rather than slide — every line was written at a different moment.
    float line = p.y * 30.0;
    float err = sin(p.y * 2.1 - t * 1.7) * u_wow
              + sin(p.y * 11.0 + t * 5.3) * u_flutter
              + (vnoise(vec2(line, t * 3.0)) - 0.5) * u_flutter * 2.0;

    vec2 q = p + vec2(err, 0.0);

    // Luma is where it should be; chroma trails it. Sampling the programme
    // three times at three displacements is exactly what a chroma delay does.
    vec3 col;
    col.r = programme(q + vec2(u_bleed, 0.0), t).r;
    col.g = programme(q, t).g;
    col.b = programme(q - vec2(u_bleed * 0.7, 0.0), t).b;

    // Dropouts: bands where the tape has lost contact. They travel slowly down
    // the frame, and they replace the picture with the tape's own noise floor
    // rather than with black — a black band is a cut, this is a fault.
    float bandId = floor(p.y * 8.0 + t * 2.0);
    float hit = step(1.0 - u_dropout * 0.35, hash11(bandId * 1.7 + floor(t * 3.0)));
    float band = smoothstep(0.5, 0.15, abs(fract(p.y * 8.0 + t * 2.0) - 0.5)) * hit;
    vec3 noise = pal(1) * (0.25 + 0.5 * hash12(vec2(uv.x * 400.0, bandId)));
    col = mix(col, noise, band * 0.7);

    // Head smear: the previous frame, dragged sideways by the same transport
    // error, is what gives tape its characteristic horizontal ghosting. The
    // programme above stands on its own, so a seek recovers immediately.
    vec3 past = prev(uv - vec2(err * 0.4 + 0.004, 0.0));
    col = mix(col, max(col, past), 0.7 * 0.55);

    // Head-switching noise at the bottom of the frame — the one part of the
    // image that is always damaged.
    float sw = smoothstep(0.06, 0.0, uv.y) * u_dropout;
    col = mix(col, pal(2) * hash12(vec2(uv.x * 300.0, floor(t * 12.0))), sw * 0.6);

    return vec4(inkValue(col), 1.0);
}
`,
};

export default analogWow;
