#ifdef GL_ES
precision mediump float;
#endif

// Canvas uniforms
uniform vec2  u_resolution;
uniform float u_uvCorr;
uniform vec2  u_mouse;
uniform vec2  u_onePixel;

// Gradient uniforms
uniform float u_aspect;
uniform vec2  u_ellipseCenter, u_majorVertex;
uniform vec2  u_focalPoint;
uniform float u_mouseStrength, u_ellipseScale;
uniform vec3  SW0, SW1, SW2, SW3;
uniform float stop0, stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8;
uniform float u_scrollProgress;

// Precomputed gradient uniforms
uniform vec2  u_majorDir;
uniform vec2  u_minorDir;
uniform float u_majorRadius;
uniform float u_minorRadius;
uniform vec2  u_invRadii;
uniform vec2  u_renderCenter;  // Center for rendering (may differ from u_ellipseCenter)

// Distortion uniforms
uniform float u_distortion;    // Max pixel offset in px (0.0 = no distortion)
uniform float u_sections;      // Number of vertical sections
uniform float u_split;         // Ratio of reversed gradient in each section

// Precomputed distortion uniforms
uniform float u_invBig;        // 1.0 / (1.0 - u_split)
uniform float u_invSplit;      // 1.0 / u_split

// Gradient color function
vec3 getColor(float t) {
    float f;
    if      (t < stop1) { return SW0; }
    else if (t < stop2) { f = clamp((t - stop1) / (stop2 - stop1), 0.0, 1.0); return mix(SW0, SW1, f); }
    else if (t < stop3) { f = clamp((t - stop2) / (stop3 - stop2), 0.0, 1.0); return mix(SW1, SW2, f); }
    else if (t < stop4) { f = clamp((t - stop3) / (stop4 - stop3), 0.0, 1.0); return mix(SW2, SW3, f); }
    else if (t < stop5) { f = clamp((t - stop4) / (stop5 - stop4), 0.0, 1.0); return mix(SW3, SW2, f); }
    else if (t < stop6) { f = clamp((t - stop5) / (stop6 - stop5), 0.0, 1.0); return mix(SW2, SW1, f); }
    else if (t < stop7) { f = clamp((t - stop6) / (stop7 - stop6), 0.0, 1.0); return mix(SW1, SW0, f); }
    else                 { return SW0; }
}

// Displacement map function
float dispMap(vec2 uv) {
    float local = fract(uv.x * max(u_sections, 1.0));
    float big = 1.0 - u_split;
    float phase = step(big, local);
    
    // White to black over [0...big]
    float val1 = 1.0 - local * u_invBig;
    // Black to white over [big...1]
    float val2 = (local - big) * u_invSplit;
    
    return mix(val1, val2, phase);
}

// Calculate gradient at given UV coordinates
vec3 calculateGradient(vec2 uv) {
    // Apply aspect ratio correction
    vec2 renderCenter = u_renderCenter;
    uv.y = (uv.y - renderCenter.y) * u_uvCorr + renderCenter.y;
    
    // Compute focal point with mouse influence
    vec2 mouseUV = u_mouse / u_resolution;
    vec2 focal = mix(u_focalPoint, mouseUV, u_mouseStrength);
    
    // Check if focal is too close to center (use ellipse center for calculations)
    vec2 focalDelta = focal - u_ellipseCenter;
    if (dot(focalDelta, focalDelta) < 1e-12) {
        focal += u_onePixel;
    }
    
    // Transform to ellipse space using render center for position
    vec2 uvDelta = uv - renderCenter;
    vec2 P = vec2(
        dot(uvDelta, u_majorDir) * u_invRadii.x,
        dot(uvDelta, u_minorDir) * u_invRadii.y
    );
    
    // Focal point in ellipse space (still relative to calculation center)
    vec2 F = vec2(
        dot(focalDelta, u_majorDir) * u_invRadii.x,
        dot(focalDelta, u_minorDir) * u_invRadii.y
    );
    
    // Clamp focal to ellipse boundary if needed
    float focalLenSq = dot(F, F);
    if (focalLenSq > 1.0) {
        F *= inversesqrt(focalLenSq);
    }
    
    // Ray-ellipse intersection
    vec2 D = P - F;
    float L = length(D);
    
    // Early exit if point is at focal
    if (L < 1e-6) {
        return SW0;
    }
    
    vec2 d = D / L;
    float b = dot(F, d);
    float c = focalLenSq - 1.0;
    float disc = b*b - c;
    
    // Compute intersection
    float T = (disc > 0.0) ? (-b + sqrt(disc)) : 1.0;
    float t = clamp(L / T, 0.0, 1.0);
    
    return getColor(t);
}

void main() {
    vec2 uv = gl_FragCoord.xy / u_resolution.xy;
    
    // Calculate displacement
    float dNorm = dispMap(uv) - 0.5;
    float offsetU = dNorm * u_distortion / u_resolution.x;
    
    // Apply displacement
    vec2 displaced = uv + vec2(offsetU, 0.0);
    displaced = clamp(displaced, 0.0, 1.0);
    
    // Calculate gradient at displaced position
    vec3 color = calculateGradient(displaced);
    
    gl_FragColor = vec4(color, 1.0);
}