// Deterministic, seedable PRNG (xorshift32 / Marsaglia).
//
// stdlib rand() is unsuitable: the per-platform C libraries ship
// different LCG constants and differing result widths (some return a
// 16-bit value, others 31 bits). Any game logic relying on
// reproducible sequences (replay, demo records, level seeds, deck
// shuffles) would diverge silently. xorshift32 has no multiply, so
// the 65816 perf floor isn't penalized by a software mul routine.

#include "joey/core.h"

// ----- Module state -----

// Initialized to 1 so jlRandom() works without an explicit seed call
// and never starts from xorshift's degenerate all-zero state.
//
// IIgs stores the state as two uint16_t halves (PERF-AUDIT #43): the
// 65816 has no 32-bit shifts, so the 13/17/5 triple lowered to per-bit
// shift helper loops (~400-700 cyc/call). The identical update on
// 16-bit halves is ~2-3x cheaper and BIT-IDENTICAL (host-proven across
// the UBER golden sequence + a 50M-state sweep), preserving the
// cross-port determinism contract in core.h. GCC ports keep the 32-bit
// form: 68000 lsl.l/lsr.l are native, so the halves form is a
// wash-to-slight-loss there (audit verifier correction).
#if defined(JOEYLIB_PLATFORM_IIGS)
static uint16_t gRngLo = 1u;
static uint16_t gRngHi = 0u;
#else
static uint32_t gRngState = 1u;
#endif

// ----- Public API (alphabetical) -----

uint32_t jlRandom(void) {
#if defined(JOEYLIB_PLATFORM_IIGS)
    uint16_t lo = gRngLo;
    uint16_t hi = gRngHi;

    // x ^= x << 13: (x << 13) halves are { hi<<13 | lo>>3, lo<<13 }.
    // Each step updates hi BEFORE lo -- the hi identity reads the
    // step's incoming lo.
    hi = (uint16_t)(hi ^ (uint16_t)((uint16_t)(hi << 13) | (uint16_t)(lo >> 3)));
    lo = (uint16_t)(lo ^ (uint16_t)(lo << 13));
    // x ^= x >> 17: (x >> 17) halves are { 0, hi>>1 } -- low half only.
    lo = (uint16_t)(lo ^ (uint16_t)(hi >> 1));
    // x ^= x << 5: (x << 5) halves are { hi<<5 | lo>>11, lo<<5 }.
    hi = (uint16_t)(hi ^ (uint16_t)((uint16_t)(hi << 5) | (uint16_t)(lo >> 11)));
    lo = (uint16_t)(lo ^ (uint16_t)(lo << 5));
    gRngLo = lo;
    gRngHi = hi;
    return ((uint32_t)hi << 16) | lo;
#else
    uint32_t x = gRngState;

    x ^= x << 13;
    x ^= x >> 17;
    x ^= x << 5;
    gRngState = x;
    return x;
#endif
}


uint16_t jlRandomRange(uint16_t bound) {
    if (bound == 0u) {
        return 0u;
    }
    // Multiply-high map over the top 16 (best-mixed) bits of the
    // xorshift state. Divide-free -- the 65816/68000 lack a hardware
    // divide, so plain `% bound` would pull in a software divide
    // routine the multiply-free PRNG was chosen to avoid. Residual
    // bias is the same negligible order as modulo.
    return (uint16_t)(((uint32_t)(jlRandom() >> 16) * (uint32_t)bound) >> 16);
}


void jlRandomSeed(uint32_t seed) {
    if (seed == 0u) {
        seed = 1u;
    }
#if defined(JOEYLIB_PLATFORM_IIGS)
    gRngLo = (uint16_t)seed;
    gRngHi = (uint16_t)(seed >> 16);
#else
    gRngState = seed;
#endif
}
