import { VisualizerInterface } from './VisualizerInterface.js';

const fragmentShader = `
uniform float u_time;
uniform float u_beat;
uniform float u_opacity;
uniform vec2 u_resolution;
uniform vec3 u_colors[16];
uniform int u_colorCount;
uniform float u_seed;
varying vec2 vUv;

// --- Utility Functions ---
float hash(float n) { return fract(sin(n) * 43758.5453123); }
vec2 rotate(vec2 p, float a) {
    float s = sin(a);
    float c = cos(a);
    return mat2(c, -s, s, c) * p;
}

// --- SDFs for Shapes ---
float sdCircle(vec2 p, float r) {
    return length(p) - r;
}

float sdStar(vec2 p, float r, float spikes) {
    float an = atan(p.y, p.x) / (2.0 * 3.14159265359);
    an = fract(an * spikes);
    float r2 = r * mix(0.6, 1.0, step(0.5, an));
    return length(p) - r2;
}

float sdSmiley(vec2 p, float r, float t, float id) {
    float d = sdCircle(p, r);
    
    // Eyes: Bigger and higher. Animate blinking.
    float eyeBlink = smoothstep(0.9, 0.95, sin(t * 0.5 + id * 1.2));
    float eyeR = 0.18 * r * (1.0 - eyeBlink * 0.9);
    vec2 eyePos = vec2(0.3, 0.35) * r;
    d = max(d, -sdCircle(vec2(abs(p.x) - eyePos.x, p.y - eyePos.y), eyeR));
    
    // Mouth: Changes shape (Smile/Surprise/Small mouth)
    float mouthType = sin(t * 1.2 + id);
    if (mouthType > 0.6) { // Surprise
        d = max(d, -sdCircle(p - vec2(0.0, -0.2) * r, 0.15 * r));
    } else if (mouthType > -0.6) { // Smile
        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)); // Open more on beat
        d = max(d, -mouth);
    } else { // Small mouth
        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);
}

// --- Main Shader ---
void main() {
    // LED Grid Setup
    float ledCountX = u_resolution.x;
    float ledCountY = u_resolution.y;
    vec2 gridUV = vec2(vUv.x * ledCountX, vUv.y * ledCountY);
    vec2 gridDeriv = fwidth(gridUV);
    float gridDensity = max(gridDeriv.x, gridDeriv.y);
    float blurFactor = smoothstep(0.3, 0.8, gridDensity);
    vec2 cell = fract(gridUV);
    vec2 uv = mix((floor(gridUV) + 0.5) / vec2(ledCountX, ledCountY), vUv, blurFactor);
    
    float distMask = distance(cell, vec2(0.5));
    float edgeSoftness = clamp(gridDensity * 1.5, 0.0, 0.5);
    float mask = 1.0 - smoothstep(0.35 - edgeSoftness, 0.45 + edgeSoftness, distMask);
    mask = mix(mask, 1.0, blurFactor);

    // Aspect correction
    vec2 p = (uv - 0.5);
    p.x *= u_resolution.x / u_resolution.y;

    float t = u_time * 0.2 + u_seed;
    float beat = u_beat;

    // --- Background: Swirling psychedelic colors ---
    vec3 colorA = vec3(0.05, 0.0, 0.1);
    vec3 colorB = vec3(0.1, 0.0, 0.2);
    if (u_colorCount >= 2) {
        colorA = u_colors[0] * 0.1;
        colorB = u_colors[int(mod(1.0, float(u_colorCount)))] * 0.15;
    }
    
    float bgNoise = sin(p.x * 3.0 + t) * cos(p.y * 3.0 - t * 0.7) + sin(length(p) * 4.0 - t);
    vec3 finalColor = mix(colorA, colorB, bgNoise * 0.5 + 0.5);
    finalColor += colorA * 0.05 * beat; // Flash background on beat

    // --- Scene Switching Logic ---
    float sceneDuration = 15.0; 
    float sceneIndex = floor(t / sceneDuration);
    float currentSceneSeed = u_seed + sceneIndex * 100.0;

    // --- "Splitting Eyes" Distortion ---
    vec2 distortionCenter = vec2(sin(t * 0.3 + currentSceneSeed) * 0.5, cos(t * 0.4 + currentSceneSeed * 1.1) * 0.5);
    float distortionRadius = 0.35 + beat * 0.15;
    float distToDistortion = length(p - distortionCenter);
    
    vec2 distortedP = p;
    if (distToDistortion < distortionRadius) {
        float strength = smoothstep(distortionRadius, 0.0, distToDistortion);
        distortedP += normalize(p - distortionCenter) * strength * (0.15 + beat * 0.2);
    }

    // --- Pre-calculate Lasers for interaction ---
    vec2 lp1[3], lp2[3];
    for (int j = 0; j < 3; j++) {
        float laserSeed = currentSceneSeed + float(j) * 789.0;
        lp1[j] = vec2(sin(t * 0.4 + laserSeed) * 0.8, cos(t * 0.3 + laserSeed * 1.1) * 0.5);
        lp2[j] = vec2(sin(t * 0.5 + laserSeed * 1.5) * 0.8, cos(t * 0.4 + laserSeed * 1.7) * 0.5);
    }

    // --- Render Symbols ---
    int numSymbols = 8 + int(mod(currentSceneSeed, 16.0));
    for (int i = 0; i < 24; i++) {
        if (i >= numSymbols) break;
        float fi = float(i);
        float symbolSeed = currentSceneSeed + fi * 123.456;
        
        // Motion: Orbital + attraction to distortion center
        float speed = 0.3 + hash(symbolSeed) * 0.4;
        vec2 basePos = vec2(
            sin(t * speed + symbolSeed) * 0.8,
            cos(t * speed * 0.8 + symbolSeed * 1.4) * 0.5
        );
        vec2 pos = mix(basePos, distortionCenter, 0.15 + beat * 0.1);
        
        vec2 sp = distortedP - pos;
        sp = rotate(sp, t * (0.1 + fract(symbolSeed * 0.7)));
        
        float d;
        float size = 0.06 + fract(symbolSeed * 0.3) * 0.04 + beat * 0.02;
        
        // Determine symbol type based on seed
        if (mod(fi + sceneIndex, 3.0) == 0.0) { // Stars
            d = sdStar(sp, size, 5.0 + floor(hash(symbolSeed * 1.2) * 3.0));
        } else if (mod(fi + sceneIndex, 3.0) == 1.0) { // Smilies
            d = sdSmiley(sp, size, u_time, fi);
        } else { // Circles
            d = sdCircle(sp, size);
        }
        
        // Interaction: Check proximity to lasers
        float laserHit = 0.0;
        for (int j = 0; j < 3; j++) {
            float distToLaser = distToSegment(pos, lp1[j], lp2[j]);
            laserHit = max(laserHit, smoothstep(0.15, 0.0, distToLaser));
        }
        
        float intensity = smoothstep(0.01, 0.0, d);
        
        // Pick a color
        vec3 symbolColor = vec3(1.0);
        if (u_colorCount > 0) {
            int colorIdx = int(mod(fi + floor(t * 0.1), float(u_colorCount)));
            symbolColor = u_colors[colorIdx];
        }
        
        // Interaction: Laser impact
        symbolColor = mix(symbolColor, vec3(1.0), laserHit * 0.8);
        float finalGlow = (0.5 + beat * 0.5 + laserHit * 2.0);
        
        finalColor = mix(finalColor, symbolColor, intensity * clamp(0.7 + beat * 0.3 + laserHit, 0.0, 1.0));
        
        // Add a slight glow/aura
        finalColor += symbolColor * (1.0 - smoothstep(0.0, size * 2.5, abs(d))) * 0.15 * finalGlow;
    }

    // --- Draw Laser Beams ---
    for (int i = 0; i < 3; i++) {
        float distToBeam = distToSegment(distortedP, lp1[i], lp2[i]);
        float beamWidth = 0.008 + beat * 0.005;
        float beamIntensity = smoothstep(beamWidth, 0.0, distToBeam);
        
        vec3 beamColor = vec3(1.0);
        if (u_colorCount > 0) {
            int colorIdx = int(mod(float(i) + floor(t * 0.2), float(u_colorCount)));
            beamColor = u_colors[colorIdx];
        }
        
        finalColor += beamColor * beamIntensity * (0.6 + beat * 0.4);
    }

    gl_FragColor = vec4(finalColor, mask * u_opacity);
}
`;

export class DruggedOutFlowVisualizer extends VisualizerInterface {
    getName() {
        return "Drugged Out Flow";
    }

    getFragmentShader() {
        return fragmentShader;
    }
}