// Shared SFX overlay mixer used by HALs whose engine outputs MOD-only
// (libxmp-lite on DOS + ST). The mixer overlays one-shot signed 8-bit
// PCM samples on top of the engine's already-rendered unsigned 8-bit
// MOD output, with a 16.16 fixed-point step so the SFX rate can
// differ from the output rate without needing an up-front resampling
// pass. Linear interpolation between adjacent source samples smooths
// the resample so low-rate SFX (~4-8 kHz) don't aliase audibly when
// played through the 22 kHz mixer output.
//
// Two slot modes:
//   * fixed buffer:  caller hands the mixer a contiguous PCM blob
//                    and the slot deactivates when read past the end.
//   * streaming:     caller supplies a fill callback; the mixer pulls
//                    chunks from the callback as it drains its small
//                    internal buffer, allowing unbounded-length SFX
//                    without pre-rendering the whole sample.
//
// Not used by Amiga (PTPlayer's mt_playfx handles SFX natively) or
// IIgs (NTPstreamsound runs its own DOC-RAM streamer).

#ifndef JOEYLIB_AUDIO_SFX_MIX_H
#define JOEYLIB_AUDIO_SFX_MIX_H

#include "joey/audio.h"
#include "joey/types.h"

// Per-streaming-slot internal prefetch buffer. Sized so refills run
// at most a few times per DMA half-buffer; the actual sample data
// is signed 8-bit so 256 bytes = 256 samples.
#define AUDIO_SFX_STREAM_BUF    256u

typedef struct {
    const uint8_t *sample;     // fixed buffer: signed 8-bit PCM, caller-owned. NULL for streaming.
    uint32_t       length;     // fixed buffer: sample bytes. Unused for streaming.
    // Read position split as a 32-bit sample index plus a 16-bit
    // fraction so the per-sample advance is 16/32-bit adds and a
    // carry compare -- no 64-bit fixed-point ops on the 68000. The
    // uint32_t index covers 4G input samples, far beyond the old
    // 48.16 counter's rationale (AGI v2 sounds run 15-48 sec and
    // need > 65535 samples). For streaming slots the position is
    // RELATIVE to the current chunk (resets on each refill).
    uint32_t       posIdx;
    uint16_t       posFrac;
    uint32_t       stepFp;     // (rateHz << 16) / outputRate
    bool           active;

    // Streaming fields. fill == NULL means this is a fixed-buffer
    // slot and the mixer ignores the stream path.
    jlAudioStreamFillT  fill;
    void               *fillCtx;
    int8_t              streamBuf[AUDIO_SFX_STREAM_BUF];
    uint16_t            streamLen;     // valid samples in streamBuf
    bool                streamEof;     // last fill returned 0
} AudioSfxSlotT;

// Arm a slot for one-shot playback from a fixed PCM buffer.
// `outputRate` is the HAL's mixer output rate; the helper computes
// stepFp from rateHz and outputRate. Sets `active = true`. No-op
// if rateHz is 0.
void audioSfxSlotArm(AudioSfxSlotT *slot,
                     const uint8_t *sample,
                     uint32_t       length,
                     uint16_t       rateHz,
                     uint32_t       outputRate);

// Arm a slot for streaming playback. `fill` will be called whenever
// the slot's internal prefetch buffer drains; it should fill `dst`
// with up to `count` signed-8-bit samples and return the count
// actually filled. Returning 0 signals end-of-stream and the slot
// auto-deactivates after consuming any remaining prefetch.
void audioSfxSlotArmStream(AudioSfxSlotT     *slot,
                           jlAudioStreamFillT fill,
                           void              *ctx,
                           uint16_t           rateHz,
                           uint32_t           outputRate);

// Overlay all active slots' contributions on top of `dst[0..len)`.
// `dst` is unsigned 8-bit centered at $80. Mix is signed-to-signed
// against (dst[i] - 128), clamped at +/-127, then biased back. Slots
// whose read position passes the sample end (or whose stream ends)
// are auto-deactivated. Linear interpolation between adjacent
// source samples keeps low-rate SFX from aliasing audibly.
//
// dst is `volatile` because the ST HAL's ISR consumes the buffer
// from interrupt context; the DOS HAL's dst is non-volatile DMA
// memory but the function accepts both via the wider qualifier.
void audioSfxOverlayMix(volatile uint8_t *dst,
                        uint16_t          len,
                        AudioSfxSlotT    *slots,
                        uint8_t           numSlots);

#endif
