// Public audio API: thin pass-through to the per-port HAL plus a
// gate that makes every call safe when audio init failed (or has not
// been called). Game code can fire-and-forget calls regardless of
// whether the platform actually has audio working at runtime.

#include <stddef.h>

#include "joey/audio.h"
#include "musicInternal.h"
#include "port.h"


static bool gAudioReady = false;


bool jlAudioInit(void) {
    if (gAudioReady) {
        return true;
    }
    gAudioReady = jlpAudioInit();
    return gAudioReady;
}


void jlAudioShutdown(void) {
    // No gAudioReady gate: every HAL self-guards its MOD/SFX teardown,
    // and the voice layer (jlAudioVoice) can hold resources -- Amiga's
    // chip-RAM square -- without jlAudioInit ever having been called.
    jlpAudioShutdown();
    gAudioReady = false;
}


void jlAudioPlayMod(const uint8_t *data, uint32_t length, bool loop) {
    if (!gAudioReady || data == NULL || length == 0) {
        return;
    }
    jlpAudioPlayMod(data, length, loop);
}


void jlAudioStopMod(void) {
    if (!gAudioReady) {
        return;
    }
    jlpAudioStopMod();
}


bool jlAudioIsPlayingMod(void) {
    if (!gAudioReady) {
        return false;
    }
    return jlpAudioIsPlayingMod();
}


void jlAudioPlaySfx(uint8_t slot, const uint8_t *sample, uint32_t length, uint16_t rateHz) {
    if (!gAudioReady || sample == NULL || length == 0) {
        return;
    }
    if (slot >= JOEY_AUDIO_SFX_SLOTS) {
        return;
    }
    jlpAudioPlaySfx(slot, sample, length, rateHz);
}


void jlAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uint16_t rateHz) {
    if (!gAudioReady || fill == NULL || rateHz == 0) {
        return;
    }
    if (slot >= JOEY_AUDIO_SFX_SLOTS) {
        return;
    }
    jlpAudioPlaySfxStream(slot, fill, ctx, rateHz);
}


void jlAudioTone(uint16_t freqHz) {
    // Deliberately NOT gated on gAudioReady -- jlAudioInit is for
    // PCM/SFX paths that need DMA + libxmp wiring. The PC speaker
    // (and other simple hardware tone generators) is direct port
    // I/O and works whether the DSP came up or not.
    jlpAudioTone(freqHz);
}


void jlAudioVoice(uint8_t voice, uint16_t freqHz, uint8_t atten) {
    // Same reasoning as jlAudioTone: direct hardware-register
    // programming, no DSP/DMA dependency. Range-check voice here so
    // the per-port HAL can assume voice < JOEY_AUDIO_VOICES.
    if (voice >= JOEY_AUDIO_VOICES) {
        return;
    }
    jlpAudioVoice(voice, freqHz, atten);
}


void jlAudioNoise(uint8_t pitch, uint8_t atten) {
    // Clamp here so the per-port HAL can assume pitch <= 31.
    if (pitch > 31u) {
        pitch = 31u;
    }
    jlpAudioNoise(pitch, atten);
}


bool jlAudioTickRegister(jlAudioTickFnT fn, uint16_t hz) {
    if (fn != NULL && hz == 0u) {
        return false;
    }
    return jlpAudioTickRegister(fn, hz);
}


void jlAudioCriticalEnter(void) {
    jlpAudioCriticalEnter();
}


void jlAudioCriticalExit(void) {
    jlpAudioCriticalExit();
}


void jlAudioStopSfx(uint8_t slot) {
    if (!gAudioReady || slot >= JOEY_AUDIO_SFX_SLOTS) {
        return;
    }
    jlpAudioStopSfx(slot);
}


void jlAudioFrameTick(void) {
    // The tracker sequencer rides the voice layer, which (like
    // jlAudioVoice itself) does not require jlAudioInit -- pump it
    // above the gAudioReady gate.
    musicPump();
    if (!gAudioReady) {
        return;
    }
#ifndef JOEYLIB_PLATFORM_IIGS
    // IIgs: NTPstreamsound is fully DOC-IRQ-driven, jlpAudioFrameTick
    // is an empty no-op there. Skip the wrapper JSL entirely on IIgs
    // so per-frame audio cost stays at the gAudioReady branch above.
    jlpAudioFrameTick();
#endif
}
