#include "audio_engine.h"
#include <android/log.h>
#include <algorithm>
#include <cstring>

#define LOG_TAG "AudioEngine"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

// --- RingBuffer ---

void RingBuffer::push(const int16_t* data, size_t frames) {
    size_t wp = write_pos_.load(std::memory_order_relaxed);
    size_t rp = read_pos_.load(std::memory_order_acquire);

    for (size_t i = 0; i < frames; i++) {
        size_t next = (wp + 1) % CAPACITY;
        if (next == rp) break; // full, drop samples
        buffer_[wp * 2]     = data[i * 2];
        buffer_[wp * 2 + 1] = data[i * 2 + 1];
        wp = next;
    }
    write_pos_.store(wp, std::memory_order_release);
}

size_t RingBuffer::pop(int16_t* out, size_t frames) {
    size_t rp = read_pos_.load(std::memory_order_relaxed);
    size_t wp = write_pos_.load(std::memory_order_acquire);

    size_t read = 0;
    while (read < frames && rp != wp) {
        out[read * 2]     = buffer_[rp * 2];
        out[read * 2 + 1] = buffer_[rp * 2 + 1];
        rp = (rp + 1) % CAPACITY;
        read++;
    }
    read_pos_.store(rp, std::memory_order_release);
    return read;
}

size_t RingBuffer::available() const {
    size_t wp = write_pos_.load(std::memory_order_acquire);
    size_t rp = read_pos_.load(std::memory_order_acquire);
    return (wp >= rp) ? (wp - rp) : (CAPACITY - rp + wp);
}

// --- AudioEngine ---

aaudio_data_callback_result_t AudioEngine::dataCallback(
        AAudioStream* /*stream*/, void* userData, void* audioData, int32_t numFrames) {
    auto* engine = static_cast<AudioEngine*>(userData);
    auto* output = static_cast<int16_t*>(audioData);

    if (!engine->isEnabled()) {
        memset(output, 0, numFrames * 2 * sizeof(int16_t));
        return AAUDIO_CALLBACK_RESULT_CONTINUE;
    }

    size_t read = engine->ringBuffer().pop(output, numFrames);
    // Fill remainder with silence to prevent noise
    if (read < static_cast<size_t>(numFrames)) {
        memset(output + read * 2, 0, (numFrames - read) * 2 * sizeof(int16_t));
    }
    return AAUDIO_CALLBACK_RESULT_CONTINUE;
}

bool AudioEngine::start(int32_t sampleRate) {
    AAudioStreamBuilder* builder = nullptr;
    aaudio_result_t result = AAudio_createStreamBuilder(&builder);
    if (result != AAUDIO_OK) {
        LOGE("Failed to create stream builder: %d", result);
        return false;
    }

    AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
    AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_SHARED);
    AAudioStreamBuilder_setSampleRate(builder, sampleRate);
    AAudioStreamBuilder_setChannelCount(builder, 2);
    AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_I16);
    AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
    AAudioStreamBuilder_setDataCallback(builder, dataCallback, this);

    result = AAudioStreamBuilder_openStream(builder, &stream_);
    AAudioStreamBuilder_delete(builder);

    if (result != AAUDIO_OK) {
        LOGE("Failed to open stream: %d", result);
        return false;
    }

    result = AAudioStream_requestStart(stream_);
    if (result != AAUDIO_OK) {
        LOGE("Failed to start stream: %d", result);
        AAudioStream_close(stream_);
        stream_ = nullptr;
        return false;
    }

    LOGD("Audio started: %d Hz stereo", sampleRate);
    return true;
}

void AudioEngine::stop() {
    if (stream_) {
        AAudioStream_requestStop(stream_);
        AAudioStream_close(stream_);
        stream_ = nullptr;
        LOGD("Audio stopped");
    }
}

void AudioEngine::pushSamples(const int16_t* data, size_t frames) {
    ring_buffer_.push(data, frames);
}
