#version 300 es
precision highp float;

uniform vec2 u_resolution;
uniform float u_time;
uniform vec2 u_pointer;          // 0–1, eased
uniform vec3 u_click;            // xy = last press (0–1), z = seconds since that press
uniform float u_pointer_down;    // 1 while pressed
uniform float u_seed;
// Declaring u_state makes this a stateful work: the runtime keeps two
// floating-point buffers and calls main() with u_pass = 0 to advance them,
// then once with u_pass = 1 to draw.
uniform sampler2D u_state;
uniform vec2 u_state_resolution;
uniform int u_pass;
uniform int u_frame;             // simulation steps since the state was reset
out vec4 outColor;

#define STATE_STEPS 12
#define STATE_SCALE 0.5
#define STATE_WARMUP 1400

uint hashu(uint x) {
    x ^= x >> 16; x *= 0x7feb352dU;
    x ^= x >> 15; x *= 0x846ca68bU;
    x ^= x >> 16;
    return x;
}

float seeded(uint k) {
    return float(hashu(uint(u_seed) * 0x9e3779b9U + k)) / 4294967295.0;
}

float dither(vec2 p) {
    return (fract(52.9829189 * fract(dot(p, vec2(0.06711056, 0.00583715)))) - 0.5) / 255.0;
}

vec2 cellAt(ivec2 p) {
    ivec2 size = ivec2(u_state_resolution);
    return texelFetch(u_state, (p + size) % size, 0).xy;
}

vec4 advance() {
    vec2 uv = gl_FragCoord.xy / u_state_resolution;
    float aspect = u_state_resolution.x / u_state_resolution.y;

    if (u_frame == 0) {
        // Seed a scatter of catalyst spores.
        float spore = 0.0;
        for (int i = 0; i < 24; i++) {
            vec2 centre = vec2(seeded(uint(10 + 2 * i)), seeded(uint(11 + 2 * i)));
            float radius = 0.015 + 0.03 * seeded(uint(80 + i));
            spore = max(spore, step(length((uv - centre) * vec2(aspect, 1.0)), radius));
        }
        return vec4(1.0, spore, 0.0, 1.0);
    }

    // Gray–Scott reaction–diffusion. The seed chooses the regime.
    int regime = int(floor(seeded(1u) * 4.0));
    vec2 rates = vec2(0.0545, 0.062);                 // coral
    if (regime == 1) rates = vec2(0.0367, 0.0649);    // dividing cells
    if (regime == 2) rates = vec2(0.029, 0.057);      // labyrinth
    if (regime == 3) rates = vec2(0.078, 0.061);      // worms
    rates.y += 0.0012 * (uv.y - 0.5);

    ivec2 p = ivec2(gl_FragCoord.xy);
    vec2 c = cellAt(p);
    vec2 laplacian = -c
        + 0.20 * (cellAt(p + ivec2(1, 0)) + cellAt(p - ivec2(1, 0)) + cellAt(p + ivec2(0, 1)) + cellAt(p - ivec2(0, 1)))
        + 0.05 * (cellAt(p + ivec2(1, 1)) + cellAt(p + ivec2(-1, 1)) + cellAt(p + ivec2(1, -1)) + cellAt(p - ivec2(1, 1)));
    float a = c.x;
    float b = c.y;
    float reaction = a * b * b;
    a += laplacian.x - reaction + rates.x * (1.0 - a);
    b += 0.5 * laplacian.y + reaction - (rates.y + rates.x) * b;

    // Press and drag to write fresh catalyst.
    vec2 toPointer = (uv - u_pointer) * vec2(aspect, 1.0);
    if (u_pointer_down > 0.5 && dot(toPointer, toPointer) < 0.0002) {
        a = 0.5;
        b = 0.25;
    }

    // Now and then a spore lands on its own, so the script never quite finishes.
    if (u_frame % 960 == 480) {
        uint k = hashu(uint(u_frame) ^ uint(u_seed));
        vec2 centre = vec2(float(k & 0xffffU), float(k >> 16)) / 65535.0;
        if (length((uv - centre) * vec2(aspect, 1.0)) < 0.025) b = 1.0;
    }
    return vec4(clamp(a, 0.0, 1.0), clamp(b, 0.0, 1.0), 0.0, 1.0);
}

vec3 render() {
    vec2 uv = gl_FragCoord.xy / u_resolution.xy;
    vec2 texel = 1.0 / u_state_resolution;
    float b = texture(u_state, uv).y;
    float bx = texture(u_state, uv + vec2(texel.x, 0.0)).y - texture(u_state, uv - vec2(texel.x, 0.0)).y;
    float by = texture(u_state, uv + vec2(0.0, texel.y)).y - texture(u_state, uv - vec2(0.0, texel.y)).y;
    vec3 normal = normalize(vec3(-6.0 * bx, -6.0 * by, 1.0));
    float light = dot(normal, normalize(vec3(-0.5, 0.6, 0.65)));

    int pick = int(floor(seeded(2u) * 3.0));
    vec3 abyss = vec3(0.012, 0.03, 0.06), coral = vec3(0.86, 0.33, 0.22), bone = vec3(0.96, 0.90, 0.80);
    if (pick == 1) { abyss = vec3(0.03, 0.02, 0.02); coral = vec3(0.78, 0.56, 0.24); bone = vec3(0.98, 0.94, 0.82); }
    if (pick == 2) { abyss = vec3(0.01, 0.04, 0.035); coral = vec3(0.16, 0.62, 0.52); bone = vec3(0.88, 0.97, 0.90); }

    float body = smoothstep(0.12, 0.32, b);
    vec3 color = mix(abyss * (0.9 + 0.3 * uv.y), coral * (0.55 + 0.6 * light), body);
    color = mix(color, bone, smoothstep(0.30, 0.46, b) * 0.55 * clamp(light, 0.0, 1.0));
    color *= 1.0 - 0.3 * smoothstep(0.55, 1.0, length(uv - 0.5) * 1.4);
    return color;
}

void main() {
    if (u_pass == 0) {
        outColor = advance();
        return;
    }
    outColor = vec4(render() + dither(gl_FragCoord.xy), 1.0);
}
