// STAGE: a procession. The protagonist, repeated across a marching lattice.
//
// The stage decides the lattice, the march and the scale falloff. It does not
// decide what is marching — that is the song's cast, and with a different
// identity this is a completely different image rather than the same image in
// another palette.

export const procession = {
    name: 'Procession',
    family: 'structural',
    kind: 'fragment',
    consumes: ['cast', 'ink', 'staging'],
    texture: 0.4,
    traits: ['shape', 'camera', 'style'],

    params: {
        columns:  { type: 'int',   range: [2, 8],     default: 4,   uniform: 'u_columns', bias: 'density' },
        depth:    { type: 'int',   range: [2, 8],     default: 4,   uniform: 'u_depth', bias: 'density' },
        march:    { type: 'float', range: [0.05, 1.2],default: 0.3, uniform: 'u_march', bias: 'motion', rate: true },
        size:     { type: 'float', range: [0.1, 0.5], default: 0.28,uniform: 'u_size' },
        stagger:  { type: 'float', range: [0, 1],     default: 0.4, uniform: 'u_stagger' },
        recede:   { type: 'float', range: [0, 1],     default: 0.5, uniform: 'u_recede' },
        spin:     { type: 'float', range: [0, 1.5],   default: 0.2, uniform: 'u_spin', rate: true },
        palette:  { type: 'palette', count: 5 },
    },

    reactive: {
        size:   { feature: 'beat', amount: 0.16, response: 'spike' },
        recede: { feature: 'bandLow', amount: 0.25 },
    },

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

    vec3 col = mix(pal(0) * 0.16, pal(1) * 0.20, uv.y);

    // Rows recede toward the horizon the track shares with every other scene.
    float horizon = sigHorizonY();

    // The formation is the song's lattice; the march is this stage's own idea
    // of what to do with it. That split is the whole point — identity owns
    // placement, the stage owns motion.
    float total = min(float(u_columns * u_depth), 64.0);

    for (int i = 0; i < 64; i++) {
        float fi = float(i);
        if (fi >= total) break;

        vec3 node = stageNode(fi, total);
        // Depth from the node's own height in the frame, so recession agrees
        // with wherever the lattice put it rather than with a row index.
        float back = clamp((node.y - (horizon - 0.9)) / 1.05, 0.0, 1.0);
        float scale = mix(1.0, 0.35, back * u_recede) * node.z;

        // The march: each element slides at a pace set by its depth, wrapping.
        float phase = fract(t * (0.4 + back * 0.6) + fi * u_stagger * 0.13);
        vec2 pos = node.xy + vec2((phase - 0.5) * 0.6, 0.0);

        vec2 q = rot(t * u_spin + fi) * (p - pos) / max(u_size * scale, 1e-3);

        float d = castMain(q) * u_size * scale;
        col = mix(col, pal(i + 2), inkMask(d, uv) * (0.35 + 0.65 * (1.0 - back)));
    }

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

export default procession;
