/*
 * MIT License
 * Author: Mark Allyn
 *
 * sweep.frag — phosphor accumulation update shader.
 *
 * For each texel in the 1024×1024 phosphor FBO:
 *   1. Decay the previous frame's energy by u_decayFactor.
 *   2. If the texel's PPI bearing falls within the current sweep arc
 *      [u_beamAnglePrev, u_beamAngle], add contributions from:
 *        - range rings (beam-painted per the P7 spec)
 *        - target echoes
 *   3. Output the updated single-channel energy value.
 *
 * PPI convention: north = +y, east = +x; bearing = atan2(x, y)
 * in degrees, clockwise from north.
 */
#version 330 core

in vec2 vTexCoord;

layout(location = 0) out vec4 fragOut;  // .r = energy; .gba unused

uniform sampler2D u_prevPhosphor;  // previous frame's energy texture (GL_R32F)
uniform float u_decayFactor;       // exp(-decay_rate * dt)
uniform float u_beamAngle;         // current beam angle, degrees CW from north
uniform float u_beamAnglePrev;     // beam angle at previous frame
uniform float u_sweepBg;           // ambient sweep-line energy (makes beam visible)
uniform float u_halfBeamDeg;       // half-beamwidth for target blobs (display widening)

// Targets: .x = range_norm (0-1), .y = bearing_deg, .z = brightness, .w = size_norm
uniform vec4  u_targets[32];
uniform int   u_targetCount;

// Range rings: up to 4 normalised radii
uniform float u_ringRadii[4];
uniform int   u_ringCount;
uniform float u_ringWidth;         // half-width in normalised range units
uniform float u_ringBrightness;

// ----------------------------------------------------------------

// Smallest unsigned angular distance between two bearings [0,360)
float angleDiff(float a, float b) {
    float d = mod(abs(a - b), 360.0);
    return (d > 180.0) ? (360.0 - d) : d;
}

// True if bearing b is inside the arc [prev, curr] swept this frame.
// Handles the 0/360 wraparound when the sweep crosses north.
bool inSweep(float b, float prev, float curr) {
    if (curr >= prev) {
        return (b >= prev && b <= curr);
    }
    // Wraparound: arc crosses 360→0
    return (b >= prev || b <= curr);
}

// ----------------------------------------------------------------

void main() {
    vec2 pos   = vTexCoord * 2.0 - 1.0;   // PPI coords: (-1,-1) SW … (+1,+1) NE
    float rng  = length(pos);

    if (rng > 1.0) {
        fragOut = vec4(0.0);
        return;
    }

    // Bearing: clockwise from north — atan2(east, north) = atan2(x, y)
    float brg = degrees(atan(pos.x, pos.y));
    if (brg < 0.0) brg += 360.0;

    // Decay previous value
    float energy = texture(u_prevPhosphor, vTexCoord).r * u_decayFactor;

    if (inSweep(brg, u_beamAnglePrev, u_beamAngle)) {

        float contrib = u_sweepBg;   // beam passage gives a faint ambient glow

        // ---- Range rings (painted at every bearing as beam sweeps) ----
        for (int i = 0; i < u_ringCount; i++) {
            float d = abs(rng - u_ringRadii[i]);
            if (d < u_ringWidth) {
                float w = 1.0 - d / u_ringWidth;
                contrib = max(contrib, u_ringBrightness * w * w);
            }
        }

        // ---- Target echoes ----
        for (int i = 0; i < u_targetCount; i++) {
            float tRng  = u_targets[i].x;
            float tBrg  = u_targets[i].y;
            float tBrt  = u_targets[i].z;
            float tSize = u_targets[i].w;

            if (tRng <= 0.0 || tBrt <= 0.0) continue;

            // Angular proximity: beam must be sweeping over the target's bearing
            float dBrg = angleDiff(brg, tBrg);
            if (dBrg >= u_halfBeamDeg) continue;

            // Range proximity: pixel must be within the target blob
            float dRng = abs(rng - tRng);
            if (dRng >= tSize) continue;

            float bw = 1.0 - dBrg / u_halfBeamDeg;         // angular taper
            float rw = 1.0 - dRng / tSize;                  // range taper
            contrib = max(contrib, tBrt * bw * rw);
        }

        energy = max(energy, contrib);
    }

    fragOut = vec4(clamp(energy, 0.0, 1.0), 0.0, 0.0, 1.0);
}
