// Structural family: a receding skyline of towers with instanced lit windows.
//
// Distinct from Ridge Terrain (rolling fbm ridgelines) and Synthwave Run (a
// ground grid): this is architecture, read as a front-on skyline. Heights and
// footprints are per-tower noise, drawn far-to-near so a nearer tower correctly
// occludes the one behind while a taller far tower still rises above a short
// one in front. Windows are lit per facade cell, so the city has interior life
// rather than reading as a lit cardboard cut-out.

export const neonCity = {
    name: 'Neon City',
    family: 'structural',
    kind: 'fragment',
    // Personality: see look/Personality.js.
    consumes: ['ink'],
    traits: ['space', 'camera', 'style'],

    params: {
        cells:    { type: 'float', range: [0.5, 4],   default: 2.0, uniform: 'u_cells', bias: 'density' },
        layers:   { type: 'int',   range: [2, 8],     default: 6,   uniform: 'u_layers', bias: 'density' },
        height:   { type: 'float', range: [0.2, 2.5], default: 1.0, uniform: 'u_height', bias: 'energy' },
        speed:    { type: 'float', range: [0.02, 0.4], default: 0.08, uniform: 'u_speed', bias: 'motion', rate: true },
        horizon:  { type: 'float', range: [-0.3, 0.3], default: 0.0, uniform: 'u_horizon' },
        reflect:  { type: 'float', range: [0, 1],     default: 0.5, uniform: 'u_reflect' },
        pulse:    { type: 'float', range: [0, 1],     default: 0.5, uniform: 'u_pulse' },
        palette:  { type: 'palette', count: 4 },
    },

    reactive: {
        height: { feature: 'bandLow', amount: 0.18, response: 'smooth' },
        pulse:  { feature: 'beat',    amount: 0.2,  response: 'smooth' },
    },

    shader: `
float towerEdge(vec2 grid, float mullions) {
    vec2 g = fract(grid);
    float steel = min(1.0 - smoothstep(0.0, 0.28 * mullions, g.x),
                      1.0 - smoothstep(0.0, 0.28 * mullions, g.y));
    return steel;
}

vec4 scene(vec2 uv, vec2 p) {
    float t = u_time * u_speed + u_seed;
    float beat = u_beat;
    p = sigCamera(p);
    // Shares the track's ground line with every other scene that has one.
    float horizon = clamp(u_horizon + sigHorizonY() * 0.3, -0.9, 0.9);

    // Sky, darkening away from the horizon glow.
    vec3 sky = mix(pal(3) * 0.06, pal(0) * 0.35,
                   smoothstep(-0.1, 0.7, p.y - horizon));
    sky *= 0.4 + 0.6 * exp(-abs(p.y - horizon) * 3.0);

    vec3 col = sky;

    for (int i = 0; i < 8; i++) {
        if (i >= u_layers) break;
        float fi = float(i);
        float depth = float(i) / max(float(u_layers - 1), 1.0); // 0 far .. 1 near
        float parallax = mix(0.25, 1.0, depth);

        // Horizontal column coordinate for this depth ring. Receding layers
        // get more cells per screen and slide slower, giving parallax.
        float xsc = mix(1.2, 3.4, depth) * u_cells;
        float xc = p.x * xsc + t * 0.18 * parallax + fi * 31.7;
        float cx = floor(xc);
        float xf = fract(xc);

        float h1 = hash11(cx * 13.1 + fi * 7.3 + u_seed);
        float h2 = hash11(cx * 61.7 + fi * 5.9 + u_seed * 0.7);

        // Footprint half width, and the tower's top above its base.
        float footprint = (0.55 + h1 * 0.35) * 0.5;         // in cell fracs
        float inTower = smoothstep(footprint + 0.04, footprint - 0.04,
                                   abs(xf - 0.5));

        float baseY = horizon - (0.05 + depth * 0.9);
        float topY = baseY + u_height * (0.35 + h1 * 1.6) * mix(0.45, 1.0, depth);
        float body = step(p.y, topY) * step(baseY, p.y);
        float tower = inTower * body;
        if (tower < 0.01) continue;

        // Facade, tinted by how far into the screen the ring sits.
        float gy = clamp((p.y - baseY) / max(topY - baseY, 1e-4), 0.0, 1.0);
        vec3 facade = mix(pal(1), pal(3), depth);

        // Instanced window grid — each facade cell decides itself lit or dark,
        // with the pulse raising the lit population and the beat underlining it.
        vec2 g = vec2(xf * 5.0, gy * 9.0);
        vec2 cellg = floor(g);
        float lit = step(0.42, hash12(cellg + fi * 3.1));
        float steel = 1.0 - min(smoothstep(0.0, 0.35, fract(g.x)),
                                smoothstep(0.0, 0.35, fract(g.y)));
        vec3 windowCol = mix(pal(2), pal(3), 0.3);
        vec3 fut = mix(col, windowCol,
                       lit * steel * (0.45 + 0.55 * u_pulse + beat * 0.25));

        // Ground-couple: darker, breath where the tower meets the street.
        fut *= 0.7 + 0.3 * smoothstep(0.0, 0.5, gy);
        // Haze pushes the far rings into the sky, the way distance actually does.
        col = mix(col, fut, tower * (1.0 - 0.45 * (1.0 - depth)));
    }

    // Street: a slate floor with a molten reflection of the nearest towers.
    if (p.y < horizon) {
        float street = smoothstep(0.0, -0.6, p.y - horizon);
        col = mix(col, pal(3) * 0.08, street);
        float refl = exp(-abs(p.y - horizon) * 4.0) * u_reflect;
        col += pal(2) * refl * (0.08 + 0.2 * beat) * street;
    }

    // Horizon haze bloom, the city's glow pooling where towers meet the sky.
    col = sigAir(col, p, smoothstep(0.0, 1.4, length(p)));

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

export default neonCity;