// TruVisuals CRT/VHS — Tracking Error
//   A horizontal band rolls vertically over the frame (TIMELINE_FRAME_INDEX).
//   Inside the band the picture is horizontally torn (per-row RAND jitter +
//   a smooth sync-bend), brightened, sprinkled with snow, slightly desaturated,
//   with a dark tear line at the band centre. Outside the band: untouched.
//   Neighbour-sampled via _tex2D. Math designed & verified in Python (identical).

DEFINE_UI_PARAMS(intensity, Intensity,    DCTLUI_SLIDER_FLOAT, 0.45, 0.0,  1.0,   0.01)
DEFINE_UI_PARAMS(bandH, Band Height,  DCTLUI_SLIDER_FLOAT, 70.0, 8.0,  400.0, 1.0)
DEFINE_UI_PARAMS(rollSpeed, Roll Speed,   DCTLUI_SLIDER_FLOAT, 4.0, -20.0, 20.0,  0.5)

// smoothstep that also works when e0 > e1 (used for the soft band edges)
__DEVICE__ float sstep(float e0, float e1, float x)
{
    float t = _saturatef((x - e0) / (e1 - e0));
    return t * t * (3.0f - 2.0f * t);
}

__DEVICE__ float3 transform(int p_Width, int p_Height, int p_X, int p_Y, __TEXTURE__ p_TexR, __TEXTURE__ p_TexG, __TEXTURE__ p_TexB)
{
    float Wf = (float)p_Width;
    float Hf = (float)p_Height;
    float y  = (float)p_Y;
    int   frame = TIMELINE_FRAME_INDEX;

    // ---- rolling band centre (rolls upward), seamless wrap ----
    float center = _fmod(-(float)frame * rollSpeed, Hf);
    if (center < 0.0f) center += Hf;
    float d = y - center;
    if (d >  Hf * 0.5f) d -= Hf;
    if (d < -Hf * 0.5f) d += Hf;
    float ad = _fabs(d);

    float halfH   = bandH * 0.5f;
    float feather = _fmaxf(2.0f, bandH * 0.22f);
    float env = sstep(halfH, halfH - feather, ad);          // 1 inside band, soft edges
    float tnorm = _saturatef(ad / _fmaxf(halfH, 1.0f));
    float bump  = 0.5f + 0.5f * _cosf(3.14159265f * tnorm);  // 1 at centre -> 0 at edge

    // ---- per-row horizontal displacement (constant across the row => a tear) ----
    float jitter = RAND((unsigned int)(p_Y * 9781u + frame * 6271u)) * 2.0f - 1.0f;
    float wander = _sinf(y * 0.12f + (float)frame * 0.3f);
    float base_amt = 20.0f * intensity;
    float jit_amt  = 22.0f * intensity;
    float row_shift = bump * (base_amt * (0.4f + 0.6f * (0.5f + 0.5f * wander)) + jit_amt * jitter);

    // sample the source displaced in x (bilinear, edge-clamped by _tex2D)
    float sampleX = _clampf((float)p_X - row_shift, 0.0f, Wf - 1.0f);
    float sampleY = y;
    float r = _tex2D(p_TexR, sampleX, sampleY);
    float g = _tex2D(p_TexG, sampleX, sampleY);
    float b = _tex2D(p_TexB, sampleX, sampleY);

    // ---- dark tear line at the band centre ----
    float tear_half = _fmaxf(1.2f, bandH * 0.035f);
    float tear = (1.0f - sstep(0.0f, tear_half, ad)) * env;   // 1 at centre line
    float dark = 1.0f - tear * (0.55f * intensity);

    // ---- brighten inside the band (excluding the tear line) ----
    float bright = 1.0f + env * (1.0f - tear) * (0.10f * intensity);

    r *= bright * dark;
    g *= bright * dark;
    b *= bright * dark;

    // ---- snow / grain (bright sparse speckle) ----
    float n = RAND((unsigned int)(frame * p_Width * p_Height + p_Y * p_Width + p_X));
    float speckle = n * n * n;
    float snow = env * (1.0f - tear) * (0.18f * intensity) * (speckle - 0.12f);
    r += snow;
    g += snow;
    b += snow;

    // ---- slight chroma loss inside the band ----
    float lum = 0.299f * r + 0.587f * g + 0.114f * b;
    float desat = env * (0.25f * intensity);
    r = r * (1.0f - desat) + lum * desat;
    g = g * (1.0f - desat) + lum * desat;
    b = b * (1.0f - desat) + lum * desat;

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