#ifndef INCLUDE_ENDER_BEAMS
#define INCLUDE_ENDER_BEAMS

/*
Ender beams ported from Fantasy Shaders, adapted to BSL's light-shaft marcher.
Sampled per ray-march step inside GetLightShafts() (lightShafts.glsl), so the
beams streak through the air and stop behind terrain, then ride the existing
volumetric-light buffer into composite1.

Relies on BSL uniforms cameraPosition and noisetex, both declared in composite.
Helper math is inlined to avoid clashing with any pow2/smoothstep1 defines.
*/

float EnderBeamNoise(vec2 planeCoord) {
    float noise  = texture2D(noisetex, planeCoord * 0.175).b;
          noise += texture2D(noisetex, planeCoord * 0.04375).b * 5.0;
    return noise;
}

vec3 DrawEnderBeams(float VdotU, vec3 playerPos) {
    const float vlFactor = 0.5; // End has no day cycle; constant stands in for FS's vlFactor

    // Vertical height: compress beams toward the horizon. 1.00 = full height, lower = shorter.
    float vU = clamp(VdotU / END_BEAM_HEIGHT, -1.0, 1.0);

    // Two-color vertical gradient (lower = horizon side, upper = toward the top),
    // mirroring the aurora's lower/upper color stops.
    vec3 lowerCol = vec3(END_BEAM_LR, END_BEAM_LG, END_BEAM_LB) / 255.0 * END_BEAM_LI;
    vec3 upperCol = vec3(END_BEAM_HR, END_BEAM_HG, END_BEAM_HB) / 255.0 * END_BEAM_HI;
    float gradT   = smoothstep(0.0, 1.0, clamp(vU, 0.0, 1.0));
    vec3 beamCol  = mix(lowerCol, upperCol, gradT);

    vec3 beamBody  = beamCol * (2.5 - vlFactor);
    vec3 beamCore1 = beamCol * (300.0 + 700.0 * vlFactor);
    vec3 beamCore2 = beamCol * (200.0 + 500.0 * vlFactor);

    int sampleCount = 8;

    float VdotUM = 1.0 - vU * vU;
    float edge   = 1.0 - abs(vU);
    edge = edge * edge;          // pow2
    edge = edge * edge;          // pow2(pow2(...))
    float VdotUM2 = VdotUM + smoothstep(0.0, 1.0, edge) * 0.2;

    vec4 beams = vec4(0.0);
    float gradientMix = 1.0;

    for (int i = 0; i < sampleCount; i++) {
        // Direction-locked (NOT world-anchored): omit cameraPosition so the beams behave
        // like a distant skybox — they stay put when you walk and only sweep when you rotate.
        vec2 planeCoord = playerPos.xz;
        planeCoord *= (1.0 + i * 6.0 / sampleCount) * 0.0014 * END_BEAM_SIZE;

        float noise = EnderBeamNoise(planeCoord);
        noise = max(0.75 - 1.0 / abs(noise - (4.0 + VdotUM * 2.0)), 0.0) * 3.0;

        if (noise > 0.0) {
            noise *= 0.65;
            float fireNoise = texture2D(noisetex, abs(planeCoord * 0.2)).b;
            noise *= 0.5 * fireNoise + 0.75;
            noise = noise * noise * 3.0 / sampleCount;
            noise *= VdotUM2;

            float f1 = fireNoise - 0.5; f1 = (f1 * f1) * (f1 * f1); // pow2(pow2(fireNoise - 0.5))
            float f2 = fireNoise - 0.3; f2 = (f2 * f2) * (f2 * f2); // pow2(pow2(fireNoise - 0.3))

            vec3 beamColor = beamBody;
            beamColor += beamCore1 * f1;
            beamColor += beamCore2 * f2;
            beamColor *= gradientMix / sampleCount;

            noise *= exp2(-6.0 * i / float(sampleCount));
            beams += vec4(noise * beamColor, noise);
        }
        gradientMix += 1.0;
    }

    beams.rgb *= beams.a * beams.a * beams.a * 3.5;
    beams.rgb = sqrt(beams.rgb);

    // Horizon brightness: concentrate the beam's brightness onto the horizon line,
    // independent of the color gradient. END_BEAM_FOCUS = strength (0 = uniform/old
    // behaviour, higher = a brighter, tighter middle band).
    float horizonFocus = 1.0 - abs(vU);
    horizonFocus *= horizonFocus;                 // smooth bell, peaks at the horizon
    beams.rgb *= max(1.0 + END_BEAM_FOCUS * (horizonFocus - 0.5), 0.0);

    // Spawn distance: cut beams within END_BEAM_START blocks of the player,
    // fading them in over a 32-block band so they appear further out.
    float beamDist = length(playerPos.xz);
    beams.rgb *= smoothstep(float(END_BEAM_START), float(END_BEAM_START) + 32.0, beamDist);

    return beams.rgb;
}

#endif //INCLUDE_ENDER_BEAMS
