#version 330 core

uniform vec3 iResolution;
uniform float iTime;
uniform float uAlpha;

out vec4 FragColor;

#define INTENSITY 1.8
#define LOOP_TIME 16.0       
#define BURST_TIME 1.5      
#define PARTICLES_PER_LAYER 8 
#define LAYERS_COUNT 8

// Fast 2D hash without heavy trigonometry
vec2 hash21(float p) {
    vec3 p3 = fract(vec3(p) * vec3(.1031, .1030, .0973));
    p3 += dot(p3, p3.yzx + 33.33);
    return fract((p3.xx + p3.yz) * p3.zy) - 0.5;
}

// Fast neon color spectrum generator
vec3 getLayerColor(float layer, float index, float cycle) {
    float seed = layer * 23.3 + index * 7.1 + cycle * 51.7;
    return 0.5 + 0.5 * cos(seed + vec3(0.0, 2.0, 4.0));
}

// Fast 2D neon line drawing with dual exponential falloff
vec3 drawNeonLine(vec2 p, vec2 a, vec2 b, vec3 col, float proximity) {
    vec2 pa = p - a, ba = b - a;
    float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
    float d = length(pa - ba * h);

    // Thin core + intense neon inner glow + wide gaseous volumetric scattering
    float core = smoothstep(0.012, 0.0, d);
    float glow = exp(-d * 40.0) * 0.3;
    float wideGlow = exp(-d * 12.0) * 0.1;

    return col * (core + glow + wideGlow) * proximity;
}

void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
    // Normalize coordinates and fix aspect ratio
    vec2 vUV = fragCoord / iResolution.xy;
    vec2 uv = vUV - 0.5;
    uv.x *= iResolution.x / iResolution.y;

    float dist = length(uv);
    float t = mod(iTime, LOOP_TIME);

    // cycleId changes every loop to guarantee completely unique forms each burst
    float cycleId = floor(iTime / LOOP_TIME); 

    float postBurstTime = max(0.0, t - BURST_TIME);
    float fadeOut = smoothstep(LOOP_TIME, LOOP_TIME - 0.6, t);

    vec3 finalColor = vec3(0.0);

    // --- 1. CHARGE AND FLASH PHASE (Instant computation) ---
    if (t < BURST_TIME) {
        float chargePulse = 0.3 + 0.15 * sin(t * 20.0);
        float chargeRadius = 0.25 * 0.35 * (1.0 + chargePulse);
        vec3 chargeColor = 0.5 + 0.5 * sin(t * 4.0 + vec3(0.0, 1.5, 3.0));
        finalColor += chargeColor * (exp(-dist * dist / max(chargeRadius * chargeRadius, 0.001)) * 0.4);
    }
    float flash = exp(-abs(t - BURST_TIME) * 8.0) * 3.0;
    finalColor += vec3(1.0, 0.96, 0.9) * flash * exp(-dist * 3.0);

    // --- 2. FAST MULTI-LAYERED BURST EXPLOSION ---
    float networkIntensity = smoothstep(0.0, 0.4, postBurstTime) * fadeOut;

    if (networkIntensity > 0.001) {
        // Global rotation matrix to simulate a spinning Pseudo-3D effect over time
        float rotAngle = iTime * 0.12;
        mat2 globalRot = mat2(cos(rotAngle), -sin(rotAngle), sin(rotAngle), cos(rotAngle));
        vec2 rotatedUV = globalRot * uv;

        // Iterate through all 8 layers sequentially to optimize GPU memory
        for (int l = 0; l < LAYERS_COUNT; l++) {
            float fl = float(l);

            // Mathematical physics scaling for 8 separate expansion speeds (layers)
            float layerExp = 0.0;
            if (l == 0)      layerExp = (1.0 - exp(-postBurstTime * 3.0)) * 0.30;
            else if (l == 1) layerExp = (1.0 - exp(-postBurstTime * 2.2)) * 0.45;
            else if (l == 2) layerExp = (1.0 - exp(-postBurstTime * 1.5)) * 0.65;
            else if (l == 3) layerExp = (1.0 - exp(-postBurstTime * 1.0)) * 0.85;
            else if (l == 4) layerExp = (1.0 - exp(-postBurstTime * 0.7)) * 1.05;
            else if (l == 5) layerExp = (1.0 - exp(-postBurstTime * 0.5)) * 1.25;
            else if (l == 6) layerExp = (1.0 - exp(-postBurstTime * 0.35)) * 1.45;
            else             layerExp = (1.0 - exp(-postBurstTime * 0.2)) * 1.70;

            // Local arrays allocated per-layer
            vec2 positions[PARTICLES_PER_LAYER];
            vec3 colors[PARTICLES_PER_LAYER];

            // Step A: Calculate unique particle nodes and assign neon spectrum values
            for (int i = 0; i < PARTICLES_PER_LAYER; i++) {
                float fi = float(i);

                // Pure deterministic seed based on layer, index and current explosion cycle
                float seed = fi * 13.33 + fl * 37.11 + cycleId * 115.79;

                vec2 dir = normalize(hash21(seed));
                float speedVar = 0.6 + 0.4 * fract(sin(seed) * 437.5);

                vec2 pos = dir * (layerExp * speedVar);

                // Add tiny organic multi-axis fluid fluctuations
                pos.x += sin(postBurstTime * 3.0 + fi) * 0.015;
                pos.y += cos(postBurstTime * 2.2 + fi * 1.5) * 0.015;

                positions[i] = pos;
                colors[i] = getLayerColor(fl, fi, cycleId);
            }

            // Step B: Construct neon connection lines and render particle cores
            for (int i = 0; i < PARTICLES_PER_LAYER; i++) {
                // Generate glowing core mask for individual sparks
                float pDist = length(rotatedUV - positions[i]);
                float coreMask = smoothstep(0.015, 0.0, pDist);
                float glowMask = exp(-pDist * 45.0) * 0.4;
                finalColor += colors[i] * (coreMask + glowMask) * networkIntensity;

                // Generate plexus/constellation lines for near neighbors inside the layer
                for (int j = i + 1; j < PARTICLES_PER_LAYER; j++) {
                    float distBetweenPoints = length(positions[i] - positions[j]);

                    // Connection distance threshold
                    if (distBetweenPoints < 0.35) {
                        float proximityFade = smoothstep(0.35, 0.05, distBetweenPoints);

                        // Chromatic interpolation between linked neon elements
                        vec3 lineColor = mix(colors[i], colors[j], 0.5);

                        // Draw flat highly optimized linear volumetric blend
                        finalColor += drawNeonLine(rotatedUV, positions[i], positions[j], lineColor, proximityFade) * networkIntensity * 1.4;
                    }
                }
            }
        }
    }

    // --- 3. EXPANDING CHROMATIC SHOCKWAVE ---
    float wavePhase = postBurstTime * 0.9;
    if (wavePhase > 0.0 && wavePhase < 1.0) {
        float ring = exp(-pow(dist - wavePhase, 2.0) / 0.0008);

        // The wave dynamically cycles colors across the RGB spectrum as it grows
        vec3 waveColor = 0.5 + 0.5 * sin(wavePhase * 5.0 + vec3(0.0, 1.5, 3.0));
        finalColor += waveColor * ring * exp(-wavePhase * 2.5) * 0.5;
    }

    // Apply smooth cinematic vignette for high neon contrast
    finalColor *= (1.0 - 0.3 * length(vUV - 0.5));
    fragColor = vec4(finalColor, 1.0);
    fragColor.a *= uAlpha;
}

void main()
{
    vec2 fragCoord = gl_FragCoord.xy;
    vec4 result;
    mainImage(result, fragCoord);
    FragColor = result;
}
