// TruVisuals - CRT / VHS Static
// Subtle animated VHS snow: fine grayscale RAND grain screened over the footage,
// plus a stronger horizontal "tracking" band that rolls vertically over time,
// with sparse coloured speckle. Per-pixel; animates via TIMELINE_FRAME_INDEX.
// Drop on a clip. All sliders are keyframeable.

DEFINE_UI_PARAMS(Amount, Amount,          DCTLUI_SLIDER_FLOAT, 0.11, 0.0, 1.0,  0.01)
DEFINE_UI_PARAMS(BandIntensity, Band Intensity,  DCTLUI_SLIDER_FLOAT, 0.28, 0.0, 1.0,  0.01)
DEFINE_UI_PARAMS(RollSpeed, Roll Speed,      DCTLUI_SLIDER_FLOAT, 6.0, -60.0, 60.0, 0.5)
DEFINE_UI_PARAMS(BandWidth, Band Width,      DCTLUI_SLIDER_FLOAT, 0.10, 0.0, 0.5,  0.01)
DEFINE_UI_PARAMS(Speckle, Speckle,         DCTLUI_SLIDER_FLOAT, 0.6,  0.0, 1.0,  0.01)

// --- baked constants (match Python design) ---
__CONSTANT__ float SPARK_GAMMA  = 2.2f;   // crisps up base grain (sparkle, not haze)
__CONSTANT__ float BAND_GAMMA   = 1.6f;   // band grain shaping
__CONSTANT__ float BAND_FEATHER = 0.5f;   // soft band edges (fraction of half-height)
__CONSTANT__ float SPARK_CHANCE = 0.0018f;// base probability of a coloured speck

__DEVICE__ float sstep(float a, float b, float x)
{
    float d = b - a;
    d = (d < 1e-6f) ? 1e-6f : d;
    float t = _saturatef((x - a) / d);
    return t * t * (3.0f - 2.0f * t);
}

__DEVICE__ float screen1(float a, float b)
{
    return 1.0f - (1.0f - a) * (1.0f - b);
}

__DEVICE__ float3 transform(int p_Width, int p_Height, int p_X, int p_Y, float p_R, float p_G, float p_B)
{
    float H = (float)p_Height;

    // --- decorrelated noise streams (mirror numpy RNG statistically) ---
    unsigned int s = (unsigned int)p_X * 1973u
                   + (unsigned int)p_Y * 9277u
                   + (unsigned int)TIMELINE_FRAME_INDEX * 26699u;
    float n_base = RAND(s);
    float n_band = RAND(s + 15485863u);
    float n_spk  = RAND(s + 32452843u);
    float c_r    = RAND(s + 49979687u);
    float c_g    = RAND(s + 67867967u);
    float c_b    = RAND(s + 86028121u);

    // --- base static: sparkle-shaped grain, screened subtly ---
    float grain = _powf(n_base, SPARK_GAMMA);
    float baseLayer = grain * Amount;

    // --- tracking band mask (rolls vertically, wraps top/bottom) ---
    float bandHalf = BandWidth * H * 0.5f;
    float center = _fmod(_fmod(RollSpeed * (float)TIMELINE_FRAME_INDEX, H) + H, H);
    float dy = _fabs((float)p_Y - center);
    dy = _fminf(dy, H - dy);                 // wrap distance
    float edge1 = bandHalf;
    float edge0 = bandHalf * (1.0f - BAND_FEATHER);
    float bandMask = 1.0f - sstep(edge0, edge1, dy);

    float bandGrain = _powf(n_band, BAND_GAMMA);
    float bandLayer = bandGrain * BandIntensity * bandMask;

    // --- screen base then band over the source (grayscale add) ---
    float r = screen1(p_R, baseLayer);
    float g = screen1(p_G, baseLayer);
    float b = screen1(p_B, baseLayer);
    r = screen1(r, bandLayer);
    g = screen1(g, bandLayer);
    b = screen1(b, bandLayer);

    // --- sparse coloured speckle (a touch denser inside the band) ---
    float thresh = 1.0f - SPARK_CHANCE * (1.0f + 3.0f * bandMask);
    if (n_spk > thresh)
    {
        float sr = (0.4f + 0.6f * c_r) * Speckle;
        float sg = (0.4f + 0.6f * c_g) * Speckle;
        float sb = (0.4f + 0.6f * c_b) * Speckle;
        r = screen1(r, sr);
        g = screen1(g, sg);
        b = screen1(b, sb);
    }

    return make_float3(_saturatef(r), _saturatef(g), _saturatef(b));
}
