#include "rive/audio/audio_source.hpp"
#ifdef WITH_RIVE_AUDIO
#include "rive/audio/audio_engine.hpp"
#include "rive/audio/audio_sound.hpp"
#include "rive/audio/audio_reader.hpp"
#include "rive/audio/audio_reader.hpp"
#endif

using namespace rive;

#ifdef WITH_RIVE_AUDIO
// Both of these should be make_rcp but it is currently getting refed directy
// somewhere else so until we refactor that we have to not ref here.
rcp<AudioSource> AudioSource::MakeAudioSource(rive::Span<uint8_t> fileBytes)
{
    ma_decoder decoder;
    ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 0, 0);
    if (ma_decoder_init_memory(fileBytes.data(),
                               fileBytes.size(),
                               &config,
                               &decoder) != MA_SUCCESS)
    {
        return nullptr;
    }
    ma_decoder_uninit(&decoder);
    return rcp<AudioSource>(new AudioSource(std::move(fileBytes)));
}

rcp<AudioSource> AudioSource::MakeAudioSource(
    rive::SimpleArray<uint8_t> fileBytes)
{
    ma_decoder decoder;
    ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 0, 0);
    auto result = ma_decoder_init_memory(fileBytes.data(),
                                         fileBytes.size(),
                                         &config,
                                         &decoder);
    if (result != MA_SUCCESS)
    {
        fprintf(stderr,
                "Failed to decode audio with error %s\n",
                ma_result_description(result));
        return nullptr;
    }

    ma_decoder_uninit(&decoder);
    return rcp<AudioSource>(new AudioSource(std::move(fileBytes)));
}

AudioSource::AudioSource(rive::Span<float> samples,
                         uint32_t numChannels,
                         uint32_t sampleRate) :
    m_isBuffered(true),
    m_channels(numChannels),
    m_sampleRate(sampleRate),
    m_ownedBytes((uint8_t*)samples.data(), samples.size() * sizeof(float))
{
    assert(numChannels != 0);
    assert(sampleRate != 0);
}

AudioSource::AudioSource(rive::Span<uint8_t> fileBytes) :
    m_isBuffered(false), m_channels(0), m_sampleRate(0), m_fileBytes(fileBytes)
{}

AudioSource::AudioSource(rive::SimpleArray<uint8_t> fileBytes) :
    m_isBuffered(false),
    m_channels(0),
    m_sampleRate(0),
    m_fileBytes(fileBytes.data(), fileBytes.size()),
    m_ownedBytes(std::move(fileBytes))
{}

const rive::Span<float> AudioSource::bufferedSamples() const
{
    assert(m_isBuffered);
    return rive::Span<float>((float*)m_ownedBytes.data(),
                             m_ownedBytes.size() / sizeof(float));
}

class AudioSourceDecoder
{
public:
    AudioSourceDecoder(rive::Span<uint8_t> fileBytes) : m_decoder({})
    {
        ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 0, 0);
        if (ma_decoder_init_memory(fileBytes.data(),
                                   fileBytes.size(),
                                   &config,
                                   &m_decoder) != MA_SUCCESS)
        {
            fprintf(stderr,
                    "AudioSourceDecoder - Failed to initialize decoder.\n");
        }
    }

    ~AudioSourceDecoder() { ma_decoder_uninit(&m_decoder); }

    uint32_t channels() { return (uint32_t)m_decoder.outputChannels; }

    uint32_t sampleRate() { return (uint32_t)m_decoder.outputSampleRate; }

private:
    ma_decoder m_decoder;
};

uint32_t AudioSource::channels()
{
    if (m_channels != 0)
    {
        return m_channels;
    }
    AudioSourceDecoder audioDecoder(m_fileBytes);
    return m_channels = audioDecoder.channels();
}

uint32_t AudioSource::sampleRate()
{
    if (m_sampleRate != 0)
    {
        return m_sampleRate;
    }
    AudioSourceDecoder audioDecoder(m_fileBytes);
    return m_sampleRate = audioDecoder.sampleRate();
}

AudioFormat AudioSource::format() const
{
    if (m_isBuffered)
    {
        return AudioFormat::buffered;
    }
    ma_decoder decoder;
    ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 0, 0);
    if (ma_decoder_init_memory(m_fileBytes.data(),
                               m_fileBytes.size(),
                               &config,
                               &decoder) != MA_SUCCESS)
    {
        fprintf(stderr,
                "AudioSource::format - Failed to initialize decoder.\n");
        return AudioFormat::unknown;
    }
    ma_encoding_format encodingFormat;

    AudioFormat format = AudioFormat::unknown;
    if (ma_decoder_get_encoding_format(&decoder, &encodingFormat) == MA_SUCCESS)
    {
        switch (encodingFormat)
        {
            case ma_encoding_format_mp3:
                format = AudioFormat::mp3;
                break;
            case ma_encoding_format_wav:
                format = AudioFormat::wav;
                break;
            case ma_encoding_format_vorbis:
                format = AudioFormat::vorbis;
                break;
            case ma_encoding_format_flac:
                format = AudioFormat::flac;
                break;
            default:
                format = AudioFormat::unknown;
                break;
        }
    }

    ma_decoder_uninit(&decoder);

    return format;
}

rcp<AudioReader> AudioSource::makeReader(uint32_t numChannels,
                                         uint32_t sampleRate)
{
    if (m_isBuffered)
    {
        return nullptr;
    }

    rive::rcp<rive::AudioSource> rcSource = rive::rcp<rive::AudioSource>(this);
    rcSource->ref();
    auto reader = rcp<AudioReader>(new AudioReader(rcSource, numChannels));

    ma_decoder_config config =
        ma_decoder_config_init(ma_format_f32, numChannels, sampleRate);

    if (ma_decoder_init_memory(m_fileBytes.data(),
                               m_fileBytes.size(),
                               &config,
                               reader->decoder()) != MA_SUCCESS)
    {
        fprintf(stderr,
                "AudioSource::makeReader - Failed to initialize decoder.\n");
        return nullptr;
    }

    return reader;
}
#else
rcp<AudioSource> AudioSource::MakeAudioSource(rive::Span<uint8_t> fileBytes)
{
    return nullptr;
}

rcp<AudioSource> AudioSource::MakeAudioSource(
    rive::SimpleArray<uint8_t> fileBytes)
{
    return nullptr;
}
AudioSource::AudioSource(rive::Span<uint8_t> fileBytes) {}
AudioSource::AudioSource(rive::SimpleArray<uint8_t> fileBytes) {}
AudioSource::AudioSource(rive::Span<float> samples,
                         uint32_t numChannels,
                         uint32_t sampleRate)
{}
uint32_t AudioSource::channels() { return 0; }
uint32_t AudioSource::sampleRate() { return 0; }
AudioFormat AudioSource::format() const { return AudioFormat::unknown; }
const rive::Span<float> AudioSource::bufferedSamples() const
{
    return rive::Span<float>(nullptr, 0);
}
#endif