// Ported from party-stage's "Drugged Out Flow".
//
// The original ran its own 15-second internal scene timer to keep itself
// interesting. That job now belongs to the arc driver, which knows where the
// song's actual transitions are — so the internal timer is gone and the scene
// variety is driven by `variant`, a param the arc can step.

export const psychedelicDrift = {
    name: 'Psychedelic Drift',
    family: 'glitch',
    kind: 'fragment',
    // Personality: see look/Personality.js.
    traits: ['shape', 'camera', 'style'],

    params: {
        count:      { type: 'int',   range: [4, 24],   default: 12,  uniform: 'u_count', bias: 'density' },
        variant:    { type: 'float', range: [0, 8],    default: 0,   uniform: 'u_variant' },
        warp:       { type: 'float', range: [0, 0.6],  default: 0.2, uniform: 'u_warp', bias: 'energy' },
        beams:      { type: 'int',   range: [0, 5],    default: 3,   uniform: 'u_beams' },
        symbolSize: { type: 'float', range: [0.03, 0.14], default: 0.07, uniform: 'u_symbolSize' },
        speed:      { type: 'float', range: [0.05, 0.8], default: 0.2, uniform: 'u_speed', bias: 'motion', rate: true },
        palette:    { type: 'palette', count: 6 },
    },

    reactive: {
        warp:       { feature: 'beat',     amount: 0.35, response: 'spike' },
        symbolSize: { feature: 'bandLow',  amount: 0.25 },
    },

    shader: `
float sdCircle(vec2 p, float r) { return length(p) - r; }

float sdStar(vec2 p, float r, float points) {
    float a = atan(p.y, p.x);
    float seg = 6.28318530718 / points;
    a = mod(a + seg * 0.5, seg) - seg * 0.5;
    return length(p) * cos(a) - r * 0.5;
}

float sdSmiley(vec2 p, float r, float t, float id) {
    float d = sdCircle(p, r);
    float blink = smoothstep(0.9, 0.95, sin(t * 0.5 + id * 1.2));
    float eyeR = 0.18 * r * (1.0 - blink * 0.9);
    d = max(d, -sdCircle(vec2(abs(p.x) - 0.3 * r, p.y - 0.35 * r), eyeR));

    float mouthType = sin(t * 1.2 + id);
    if (mouthType > 0.6) {
        d = max(d, -sdCircle(p - vec2(0.0, -0.2) * r, 0.15 * r));
    } else if (mouthType > -0.6) {
        float mouth = sdCircle(p - vec2(0.0, -0.1) * r, 0.45 * r);
        mouth = max(mouth, -(p.y - (0.05 + 0.1 * u_beat) * r));
        d = max(d, -mouth);
    } else {
        d = max(d, -sdCircle(p - vec2(0.0, -0.25) * r, 0.08 * r));
    }
    return d;
}

float distToSegment(vec2 p, vec2 a, vec2 b) {
    vec2 pa = p - a, ba = b - a;
    float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
    return length(pa - ba * h);
}

vec4 scene(vec2 uv, vec2 p) {
    float t = u_time * u_speed + u_seed;
    float beat = u_beat;
    float vs = u_seed + floor(u_variant) * 100.0;

    // Swirling background.
    p = sigCamera(p);
    float bg = sin(p.x * 3.0 + t) * cos(p.y * 3.0 - t * 0.7) + sin(length(p) * 4.0 - t);
    vec3 col = mix(pal(0) * 0.12, pal(1) * 0.18, bg * 0.5 + 0.5);
    col += pal(0) * 0.05 * beat;

    // Lens distortion that drags everything toward a wandering centre.
    vec2 centre = vec2(sin(t * 0.3 + vs) * 0.5, cos(t * 0.4 + vs * 1.1) * 0.5);
    float radius = 0.35 + beat * 0.15;
    float dc = length(p - centre);
    vec2 dp = p;
    if (dc < radius && dc > 1e-4) {
        dp += normalize(p - centre) * smoothstep(radius, 0.0, dc) * (u_warp + beat * 0.2);
    }

    // Beam endpoints, precomputed so symbols can react to them.
    vec2 b1[5], b2[5];
    for (int j = 0; j < 5; j++) {
        float ls = vs + float(j) * 789.0;
        b1[j] = vec2(sin(t * 0.4 + ls) * 0.8, cos(t * 0.3 + ls * 1.1) * 0.5);
        b2[j] = vec2(sin(t * 0.5 + ls * 1.5) * 0.8, cos(t * 0.4 + ls * 1.7) * 0.5);
    }

    for (int i = 0; i < 24; i++) {
        if (i >= u_count) break;
        float fi = float(i);
        float s = vs + fi * 123.456;

        float speed = 0.3 + hash11(s) * 0.4;
        vec2 basePos = vec2(sin(t * speed + s) * 0.8, cos(t * speed * 0.8 + s * 1.4) * 0.5);
        vec2 pos = mix(basePos, centre, 0.15 + beat * 0.1);

        vec2 sp = rot(t * (0.1 + fract(s * 0.7))) * (dp - pos);
        float size = u_symbolSize * (0.7 + fract(s * 0.3) * 0.6);

        float d;
        float kind = mod(fi + floor(u_variant), 3.0);
        // One of the three symbol kinds is the track's own form, so its cast of
        // characters includes the one every other scene is built from.
        if (kind == 0.0)      d = sdStar(sp, size, 5.0 + floor(hash11(s * 1.2) * 3.0));
        else if (kind == 1.0) d = sdSmiley(sp, size, u_time, fi);
        else                  d = sigShape(sp / max(size, 1e-3)) * size;

        float hit = 0.0;
        for (int j = 0; j < 5; j++) {
            if (j >= u_beams) break;
            hit = max(hit, smoothstep(0.15, 0.0, distToSegment(pos, b1[j], b2[j])));
        }

        vec3 sc = mix(pal(i + int(floor(t * 0.3))), vec3(1.0), hit * 0.8);
        float intensity = smoothstep(0.012, 0.0, d);
        col = mix(col, sc, intensity * sat(0.7 + beat * 0.3 + hit));
        col += sc * (1.0 - smoothstep(0.0, size * 2.5, abs(d))) * 0.15 * (0.5 + beat * 0.5 + hit * 2.0);
    }

    for (int i = 0; i < 5; i++) {
        if (i >= u_beams) break;
        float w = 0.008 + beat * 0.005;
        float intensity = smoothstep(w, 0.0, distToSegment(dp, b1[i], b2[i]));
        col += pal(i + 2) * intensity * (0.6 + beat);
    }

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

export default psychedelicDrift;
