#version 330 core
out vec4 FragColor;
in vec2 vUV;

uniform vec3 iResolution;
uniform float iTime;
uniform float uAlpha;
uniform sampler2D iChannel0;

vec3 getPathPosition(float z)
{
    return vec3(12.0 * cos(z * vec2(0.1, 0.12)), z);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
    vec2 uv = (fragCoord - iResolution.xy * 0.5) / iResolution.y;

    float bass = texture(iChannel0, vec2(0.05, 0.25)).x;
    float mid = texture(iChannel0, vec2(0.5, 0.25)).x;
    float treble = texture(iChannel0, vec2(0.8, 0.25)).x;

    float animTime = iTime * 4.0 + 5.0 + 5.0 * sin(iTime * 0.3);

    vec3 rayOrigin = getPathPosition(animTime);
    vec3 lookTarget = getPathPosition(animTime + 4.0);

    vec3 forward = normalize(lookTarget - rayOrigin);
    vec3 right = normalize(vec3(-forward.z, 0.0, forward.x));
    vec3 up = cross(forward, right);
    vec3 rayDir = normalize(uv.x * right + uv.y * up + forward);

    float stepDist = 1.0;
    float totalDist = 0.0;
    float orbDist = 1.0;
    vec3 accumulatedColor = vec3(0.0);
    vec3 rayPos = rayOrigin;

    vec3 babyBlue = vec3(0.3, 0.8, 1.0);
    vec3 hotPink = vec3(1.0, 0.1, 0.6);
    vec3 neonOrange = vec3(1.0, 0.4, 0.0);
    vec3 tropGreen = vec3(0.1, 0.9, 0.4);

    for (float i = 1.0; i <= 28.0; i++)
    {
        if (totalDist >= 30.0)
            break;

        rayPos += rayDir * stepDist;

        vec3 pathCenter = getPathPosition(rayPos.z);
        float sineTime = sin(iTime);

        vec3 orbCenter = vec3(
            pathCenter.x + sineTime,
            pathCenter.y + sineTime * 2.0,
            6.0 + animTime + sineTime * 2.0
        );
        orbDist = length(rayPos - orbCenter) - (0.01 + bass * 0.8);

        float baseRadius = cos(rayPos.z * 0.6) * 2.0 + 4.0 + (mid * 0.5);

        float tunnelStructure = min(
            length(rayPos.xy - pathCenter.x - 6.0),
            length((rayPos - pathCenter).xy)
        );

        float largeScoops = abs(dot(sin(0.4 * rayPos), vec3(0.25))) / 0.1;
        float detailTexture = abs(dot(sin(animTime + 16.0 * rayPos + treble * 5.0), vec3(0.22))) / 2.0;

        float carvedDist = baseRadius - tunnelStructure + largeScoops + detailTexture;

        vec3 fluidPos = rayPos;
        for (float j = 1.0; j <= 7.0; j++)
        {
            fluidPos += sin(fluidPos.yzx * j + iTime + (mid * 2.0) + 0.5 * i) / j;
        }

        float fluidTunnelDist = 0.4 * length(vec4(0.3 * cos(fluidPos) - 0.3, carvedDist));

        stepDist = max(0.001, min(orbDist, fluidTunnelDist));
        totalDist += stepDist;

        float rawSine = sin(iTime * 0.4) * 0.5 + 0.5;
        float timeBlend = smoothstep(0.45, 0.55, rawSine);

        vec3 activeColor1 = mix(babyBlue, neonOrange, timeBlend);
        vec3 activeColor2 = mix(hotPink, tropGreen, timeBlend);

        float phase = fluidPos.y + i * 0.4 - iTime;
        float swirl = smoothstep(0.4, 0.6, sin(phase * 2.0) * 0.5 + 0.5);

        float beatTrigger = clamp(pow(bass, 2.5) + (mid * 0.4), 0.0, 1.0);
        float colorMix = clamp(swirl + beatTrigger, 0.0, 1.0);

        vec3 palette = mix(activeColor1, activeColor2, colorMix);
        palette *= 1.0 + (bass * 0.5);

        float wallLight = 0.8 / (stepDist * stepDist * 8.0 + 0.05);
        float orbLight = 2.0 / (orbDist * orbDist * 5.0 + 0.05);

        accumulatedColor += palette * (wallLight + orbLight) / (i * 0.8);
    }

    fragColor = vec4(1.0 - exp(-accumulatedColor * 0.35), uAlpha);
    fragColor.rgb *= smoothstep(1.5, 0.35, length(uv));
}

void main()
{
    mainImage(FragColor, gl_FragCoord.xy);
}
