// Glitch family: a datamosh. The frame is cut into a coarse grid of blocks and
// each block is pulled along a per-block stroke drawn from the PREVIOUS frame —
// so the corruption accrues and slithers across frames instead of tearing once.
//
// Everything steps on one quantised grid: the block field re-rolls on bar lines
// rather than crawling, which is what makes this read as an edit rather than as
// a smooth warp. On an onset the spill widens, which is the "hit".

export const blockMosh = {
    name: 'Block Mosh',
    family: 'glitch',
    kind: 'fragment',
    // Personality: see look/Personality.js.
    traits: ['camera', 'style'],

    params: {
        block:    { type: 'float', range: [4, 48],  default: 22,  uniform: 'u_blocks', bias: 'density' },
        smear:    { type: 'float', range: [0, 0.5], default: 0.18, uniform: 'u_smear', bias: 'energy' },
        quantize: { type: 'float', range: [1, 8],   default: 4,   uniform: 'u_quantize' },
        bleed:    { type: 'float', range: [0, 1],   default: 0.45, uniform: 'u_bleed' },
        speed:    { type: 'float', range: [0.1, 1.4], default: 0.5, uniform: 'u_speed', bias: 'motion', rate: true },
        burst:    { type: 'float', range: [0, 0.4], default: 0.12, uniform: 'u_burst' },
        palette:  { type: 'palette', count: 4 },
    },

    reactive: {
        smear: { feature: 'bandHigh', amount: 0.3,  response: 'smooth' },
        bleed: { feature: 'flux',     amount: 0.2,  response: 'spike' },
        burst: { feature: 'beat',     amount: 0.25, response: 'smooth' },
    },

    shader: `
vec4 scene(vec2 uv, vec2 p) {
    float t = u_time * u_speed + u_seed;
    float beat = u_beat;

    // The camera moves the CONTENT, not the block grid. The grid has to stay
    // screen-aligned or the mosh stops reading as a codec artefact and starts
    // reading as a moving texture — but the field being moshed is part of the
    // shot and is filmed by the same operator as every other scene. (Assigning
    // sigCamera(p) to an otherwise unused p is how this scene originally
    // "honoured" the trait; the Phase 9 render gate measured the resulting
    // difference at exactly zero.)
    vec2 cp = sigCamera(p);

    // The thing being moshed: a sheared banded field, recoloured per track, so
    // the corruption has real content to drag around even standing alone.
    float shear = cp.y * (2.5 + u_sigLine * 3.0) + t * 0.45;
    float field = fbm(vec2(cp.x * 0.9 + sin(shear) * 0.3, cp.y * 1.5 + t * 0.35), 3);
    vec3 base = palRamp(field * 1.15 + cp.x * 0.18 + t * 0.03);
    base *= 0.35 + 0.55 * sat(field * 1.5);

    // One quantised grid for every jump below, so the mosh steps like an edit
    // rather than crawling like a smooth warp.
    float q = max(u_quantize, 1.0);
    float st = floor(u_barPhase * q * 6.0) + floor(t * 4.0) * (q * 6.0);
    vec2 cell = floor(uv * u_blocks);

    // Each block gets one fixed stroke per step: a direction and a reach.
    vec2 stroke = (hash22(cell + st * 31.0) - 0.5) * 2.0;
    float reach = hash12(cell + st * 17.0);
    vec2 dst = clamp(uv + stroke * u_smear * (0.3 + reach), 0.0, 1.0);

    // The mosh pulls the PREVIOUS frame along the stroke and layers it over the
    // fresh field. The previous frame is itself moshed, so smear accrues.
    vec3 mosh = prev(dst);
    float m = clamp(u_bleed * (0.5 + reach), 0.0, 1.0);
    vec3 col = mix(base, mosh, m);

    // Weak block tint, pulse-pinned to the beat but kept small — the frame as a
    // whole never swings in luminance, so it stays clear of the WCAG 2.3.1
    // flash ceiling even on a hard four-on-the-floor.
    float blk = hash12(cell + st * 7.0);
    col += pal(2) * u_burst * (0.5 + 0.5 * blk) * (0.5 + 0.5 * beat);

    col += sigGrain(uv);
    return vec4(col, 1.0);
}
`,
};

export default blockMosh;
