// Structural family: horizontal belts of cargo running in alternating
// directions, stacked up the frame.
//
// Distinct from Neon City (a static skyline) and Pylon Grid (perspective depth):
// this has no depth at all. It is flat, industrial and lateral — the only scene
// in the library whose motion is purely sideways, which is exactly what makes it
// cut well against everything that recedes.
//
// Each crate is stamped in the track's signature form, and the belts step in
// bar-quantised lurches rather than sliding, so the movement is mechanical.

export const cargoBelt = {
    name: 'Cargo Belt',
    family: 'structural',
    kind: 'fragment',
    // Takes the track's surface grain, but lightly — this is drawn, not filmed.
    texture: 0.4,
    traits: ['shape', 'camera', 'style'],

    params: {
        belts:    { type: 'int',   range: [2, 8],      default: 4,    uniform: 'u_belts', bias: 'density' },
        crates:   { type: 'float', range: [2, 14],     default: 6,    uniform: 'u_crates', bias: 'density' },
        crateSize:{ type: 'float', range: [0.15, 0.75],default: 0.42, uniform: 'u_crateSize' },
        speed:    { type: 'float', range: [0.05, 1.0], default: 0.3,  uniform: 'u_speed', bias: 'motion', rate: true },
        gap:      { type: 'float', range: [0.02, 0.3], default: 0.1,  uniform: 'u_gap' },
        rails:    { type: 'float', range: [0, 1],      default: 0.45, uniform: 'u_rails' },
        lamp:     { type: 'float', range: [0, 1.3],    default: 0.5,  uniform: 'u_lamp', bias: 'energy' },
        palette:  { type: 'palette', count: 5 },
    },

    reactive: {
        lamp:  { feature: 'beat',    amount: 0.35, response: 'spike' },
        rails: { feature: 'bandLow', amount: 0.2,  response: 'smooth' },
    },

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

    vec3 col = pal(0) * 0.05;

    float span = 2.0 / max(float(u_belts), 1.0);

    for (int i = 0; i < 8; i++) {
        if (i >= u_belts) break;
        float fi = float(i);
        float centre = -1.0 + span * (fi + 0.5);
        float dy = p.y - centre;
        if (abs(dy) > span * 0.5) continue;

        float dir = mod(fi, 2.0) < 0.5 ? 1.0 : -1.0;

        // Quantised travel: the belt advances in eighth-bar steps, so cargo
        // lurches from cell to cell the way a conveyor does.
        float march = floor((t + fi * 0.37) * 4.0) * 0.25 * dir;
        float lane = p.x * 0.5 + march;

        float cell = floor(lane * u_crates);
        float withinCell = fract(lane * u_crates);
        float rnd = hash12(vec2(cell, fi));

        // Not every cell carries a crate; the gaps are what make it read as
        // cargo rather than as a stripe pattern.
        if (rnd > u_gap) {
            vec2 local = vec2((withinCell - 0.5) * 2.0, dy / max(span * 0.5, 1e-3));
            float size = u_crateSize * (0.7 + rnd * 0.5);
            float d = sigShape(local / max(size, 1e-3)) * size;

            vec3 crateColor = pal(int(mod(cell + fi, 4.0)) + 1);
            col = mix(col, crateColor * (0.35 + rnd * 0.5), smoothstep(0.02, -0.02, d));
            col += crateColor * sigEdge(d) * (0.4 + u_lamp * 0.5);

            // Lamp on a minority of crates, pulsing on the beat. Local, not
            // whole-frame: a per-crate blink is not a flash.
            if (rnd > 0.82) col += pal(4) * smoothstep(0.06, 0.0, length(local)) * u_lamp;
        }

        // Belt rails, top and bottom of each lane.
        float rail = smoothstep(0.06, 0.0, abs(abs(dy) - span * 0.45));
        col += pal(2) * rail * u_rails * 0.5;
    }

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

export default cargoBelt;
