/*
// Created by bruno on 16.2.2025.
*/

#include "audio.h"

int pcm_buffer_empty(PcmVoice *pcm) {
    return pcm->head == pcm->tail;
}

int pcm_buffer_full(PcmVoice *pcm) {
    return ((pcm->tail + 1) % PCM_BUFFER_SIZE) == pcm->head;
}

void pcm_buffer_push(PcmVoice *pcm, uint16_t sample) {
    if (!pcm_buffer_full(pcm)) {
        pcm->buffer[pcm->tail] = sample;
        pcm->tail = (pcm->tail + 1) % PCM_BUFFER_SIZE;
    }
}

uint16_t pcm_buffer_pop(PcmVoice *pcm) {
    if (!pcm_buffer_empty(pcm)) {
        uint16_t sample = pcm->buffer[pcm->head];
        pcm->head = (pcm->head + 1) % PCM_BUFFER_SIZE;
        return sample;
    }
    return 0;
}

void audio_callback(void *userdata, Uint8 *stream, int len) {
    AudioData *audio = (AudioData *) userdata;
    int samples = len / sizeof(float);

    for (int i = 0; i < samples; i++) {
        float mix = 0.0f;
        int activeVoices = 0;

        for (int v = 0; v < NUM_SYNTH_VOICES; v++) {
            SynthVoice *voice = &audio->synthVoices[v];
            if (voice->volume == 0 || voice->frequency == 0) continue;

            float sample = 0.0f;
            float t = (float) voice->phase / 255.0f * 2.0f - 1.0f;

            switch (voice->waveform) {
                default:
                case WAVE_SINE:
                    sample = sinf(voice->phase * 2.0f * M_PI / 256.0f);
                    break;
                case WAVE_SQUARE:
                    sample = (t >= 0.0f) ? 1.0f : -1.0f;
                    break;
                case WAVE_SAWTOOTH:
                    sample = t;
                    break;
                case WAVE_TRIANGLE:
                    sample = (t < 0) ? -t : t;
                    break;
                case WAVE_NOISE:
                    sample = ((float) rand() / RAND_MAX) * 2.0f - 1.0f;
                    break;
            }

            voice->phase += (uint8_t) ((voice->frequency * 256) / SAMPLE_RATE);
            mix += sample * (voice->volume / 255.0f);
            activeVoices++;
        }

        PcmVoice *pcm = &audio->pcmVoice;
        if (pcm->volume > 0 && !pcm_buffer_empty(pcm)) {
            float pcmSample = pcm_buffer_pop(pcm) / 65535.0f;
            mix += pcmSample * (pcm->volume / 255.0f);
            activeVoices++;
        }

        ((float *) stream)[i] = (activeVoices > 0) ? mix / activeVoices : 0.0f;
    }
}
