// JoeyLib lifecycle: configuration, initialization, shutdown.

#ifndef JOEYLIB_CORE_H
#define JOEYLIB_CORE_H

#include "platform.h"
#include "types.h"

typedef struct {
    // Runtime compiled-sprite cache size. 0 selects a per-platform
    // default: 8 KB on IIgs, 32 KB on Amiga/ST/DOS (the 68k/x86
    // emitters are ~4-8x less code-dense than the 65816's, so a
    // 32x32 sprite can need 8-16 KB there). Size against
    // jlSpriteCompiledSize() when tuning. On the IIgs the cache is one
    // Memory Manager block allocated with attrNoCross (it may not span
    // a 64 KB bank boundary), so values above 65536 are clamped to
    // 65536; even a clamped request can fail if no free bank exists.
    uint32_t  codegenBytes;
    uint32_t  audioBytes;     // reserved; not yet consulted by any audio engine
} jlConfigT;

// Initialize the library. Returns true on success.
// On failure, jlLastError() returns a human-readable description.
bool jlInit(const jlConfigT *config);

// Shut down the library, releasing all resources.
void jlShutdown(void);

// Returns the most recent error message, or NULL if none.
const char *jlLastError(void);

// Returns the platform identifier string (e.g., "Apple IIgs", "MS-DOS").
const char *jlPlatformName(void);

// Returns the library version string (e.g., "1.0.0").
const char *jlVersionString(void);

// Allocate a large or persistent buffer, backed by the platform's
// native allocator rather than the small C heap. Use this (not malloc)
// for anything that exceeds the per-allocation or total ceiling of the
// stock C heap -- on the IIgs the C heap lives in bank 0 and is tiny,
// and a single malloc is capped at ~32 KB, so multi-KB caches and frame
// buffers must come from the IIgs Memory Manager instead. Returns NULL
// on failure. The returned pointer is NOT zero-filled.
void *jlAlloc(uint32_t bytes);

// Release a buffer returned by jlAlloc. NULL is accepted and ignored.
void jlFree(void *p);

// Block the calling thread until the next display vertical blank.
// Used to pace game loops to the display's native refresh rate
// (~70 Hz on VGA mode 13h, ~50 or ~60 Hz on Amiga/ST PAL/NTSC, ~60 Hz
// on IIgs SHR). Cheap on every port since the underlying mechanism is
// always a hardware-level wait, not a software timer.
void jlWaitVBL(void);

// Monotonic 16-bit frame counter. Caller must poll faster than
// 2 * jlFrameHz() so no edge is missed. Used by benchmarks and
// frame-rate-independent animation. (Per-port edge-detect / VBL-ISR
// mechanism is documented at jlpFrameCount in src/core/port.h.)
uint16_t jlFrameCount(void);

// Nominal display frame rate in Hz: 50 (Amiga PAL), 60 (IIgs / ST
// NTSC default), 70 (VGA mode 13h). The actual VBL cadence may
// drift slightly; the value reported here is what benchmarks divide
// by to convert iters-per-N-frames to ops/sec.
uint16_t jlFrameHz(void);

// Monotonic wall-clock millisecond counter since jlInit. On DOS/ST it
// reads a real hardware timer; on IIgs/Amiga the generic implementation
// derives it from the frame counter (frame-granularity steps, and the
// caller must poll at least once per 65535 frames -- ~18 min at 60Hz --
// or wrapped frames are lost). Two consecutive calls separated by a long
// render report the true elapsed time, not the number of VBLs that
// fired. (Per-port millisecond timer sources are documented at
// jlpMillisElapsed in src/core/port.h.)
uint32_t jlMillisElapsed(void);

// Deterministic, seedable pseudo-random generator. Bit-identical
// output across every JoeyLib port for a given seed, since stdlib
// rand() varies wildly between platforms and C libraries.
// The underlying algorithm is xorshift32 (Marsaglia): period 2^32-1,
// no multiply, suitable for games but not for cryptography.
uint32_t jlRandom(void);

// Return a value in [0, bound). bound == 0 returns 0.
uint16_t jlRandomRange(uint16_t bound);

// Seed the generator. Any 32-bit value is accepted; a seed of 0 is
// remapped to 1 since xorshift cannot escape the all-zero state.
void jlRandomSeed(uint32_t seed);

#endif
