// Standalone tests for reasampler::util::elapsedAtLeast — no VST3, no REAPER, no framework.
// Same fast assert loop as the sibling pure tests.
//
// Covers: the ordinary in-range case; the exact boundary (elapsed == interval opens the
// gate, elapsed == interval - 1 does not); the zero/zero first-call case; and the
// GetTickCount() WRAPAROUND — the one property in this idiom a reader has to think about.
// A `now` that has numerically wrapped past `since` must still open the gate once the true
// forward elapsed time reaches `interval`, never wedge it shut.

#include "../src/core/util/elapsed_at_least.h"

#include <cstdio>
#include <limits>

using namespace reasampler::util;

static int g_fail = 0;
#define CHECK(cond) do { if(!(cond)) { \
    std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)

static void testOrdinaryCase() {
    CHECK(elapsedAtLeast(1000u, 0u, 500u));       // well past the interval
    CHECK(!elapsedAtLeast(400u, 0u, 500u));        // not there yet
}

static void testExactBoundary() {
    CHECK(elapsedAtLeast(500u, 0u, 500u));         // exactly the interval: gate opens
    CHECK(!elapsedAtLeast(499u, 0u, 500u));        // one ms short: gate stays shut
}

static void testZeroZeroFirstCallOpensImmediately() {
    // A zero-initialized "last fired" tick (lastDetectTick's documented default) must not
    // block the very first call.
    CHECK(elapsedAtLeast(0u, 0u, 0u));
    CHECK(elapsedAtLeast(1u, 0u, 0u));
}

// GetTickCount() wraps every ~49.7 days (2^32 ms). `since` sits near the top of the range;
// `now` has wrapped back around to a small value. Unsigned subtraction computes the correct
// forward distance (the gap to UINT_MAX plus the distance past zero) rather than reading as
// a huge negative gap that a signed/naive comparison would misinterpret as "not yet".
static void testWraparoundOpensRatherThanWedgesShut() {
    const unsigned int kMax = std::numeric_limits<unsigned int>::max();
    const unsigned int since = kMax - 49u;  // 50 ms of headroom before the counter wraps
    const unsigned int interval = 500u;

    // 450 ms after the wrap: 51 ms (headroom to wrap) + 450 ms = 501 ms true elapsed -> open.
    CHECK(elapsedAtLeast(450u, since, interval));
    // 439 ms after the wrap: 51 + 439 = 490 ms true elapsed -> still shut.
    CHECK(!elapsedAtLeast(439u, since, interval));
    // Exactly at the wrap instant (now == 0): 51 ms true elapsed -> shut.
    CHECK(!elapsedAtLeast(0u, since, interval));
    // now == since (no time passed, mid-wrap-approach): never wedges into "always open".
    CHECK(!elapsedAtLeast(since, since, interval));
}

int main() {
    testOrdinaryCase();
    testExactBoundary();
    testZeroZeroFirstCallOpensImmediately();
    testWraparoundOpensRatherThanWedgesShut();
    if (g_fail == 0) std::printf("elapsed_at_least: all tests passed\n");
    else std::printf("elapsed_at_least: %d FAILED\n", g_fail);
    return g_fail == 0 ? 0 : 1;
}
