#ifdef WITH_RIVE_AUDIO
#ifndef _RIVE_AUDIO_SOUND_HPP_
#define _RIVE_AUDIO_SOUND_HPP_

#include "miniaudio.h"
#include "rive/refcnt.hpp"
#include "rive/audio/audio_source.hpp"

namespace rive
{
class AudioEngine;
class Artboard;

struct ma_end_clipped_decoder
{
    ma_data_source_base base;
    ma_decoder decoder;
    ma_uint64 frameCursor;
    ma_uint64 endFrame;
};

static ma_result ma_end_clipped_decoder_read(ma_data_source* pDataSource,
                                             void* pFramesOut,
                                             ma_uint64 frameCount,
                                             ma_uint64* pFramesRead)
{
    ma_end_clipped_decoder* clipped = (ma_end_clipped_decoder*)pDataSource;

    ma_result result = ma_decoder_read_pcm_frames(&clipped->decoder,
                                                  pFramesOut,
                                                  frameCount,
                                                  pFramesRead);

    clipped->frameCursor += *pFramesRead;
    if (clipped->frameCursor >= clipped->endFrame)
    {
        ma_uint64 overflow = clipped->frameCursor - clipped->endFrame;
        if (*pFramesRead > overflow)
        {
            *pFramesRead -= overflow;
        }
        else
        {
            *pFramesRead = 0;
            return MA_AT_END;
        }
    }

    return result;
}

static ma_result ma_end_clipped_decoder_seek(ma_data_source* pDataSource,
                                             ma_uint64 frameIndex)
{
    ma_end_clipped_decoder* clipped = (ma_end_clipped_decoder*)pDataSource;
    ma_result result =
        ma_decoder_seek_to_pcm_frame(&clipped->decoder, frameIndex);
    if (result != MA_SUCCESS)
    {
        return result;
    }

    clipped->frameCursor = frameIndex;
    return result;
}

static ma_result ma_end_clipped_decoder_get_data_format(
    ma_data_source* pDataSource,
    ma_format* pFormat,
    ma_uint32* pChannels,
    ma_uint32* pSampleRate,
    ma_channel* pChannelMap,
    size_t channelMapCap)
{
    ma_end_clipped_decoder* clipped = (ma_end_clipped_decoder*)pDataSource;
    return ma_decoder_get_data_format(&clipped->decoder,
                                      pFormat,
                                      pChannels,
                                      pSampleRate,
                                      pChannelMap,
                                      channelMapCap);
}

static ma_result ma_end_clipped_decoder_get_cursor(ma_data_source* pDataSource,
                                                   ma_uint64* pCursor)
{
    ma_end_clipped_decoder* clipped = (ma_end_clipped_decoder*)pDataSource;
    *pCursor = clipped->frameCursor;
    return MA_SUCCESS;
}

static ma_result ma_end_clipped_decoder_get_length(ma_data_source* pDataSource,
                                                   ma_uint64* pLength)
{
    ma_end_clipped_decoder* clipped = (ma_end_clipped_decoder*)pDataSource;
    return ma_decoder_get_length_in_pcm_frames(&clipped->decoder, pLength);
}

static ma_data_source_vtable g_ma_end_clipped_decoder_vtable = {
    ma_end_clipped_decoder_read,
    ma_end_clipped_decoder_seek,
    ma_end_clipped_decoder_get_data_format,
    ma_end_clipped_decoder_get_cursor,
    ma_end_clipped_decoder_get_length};

class AudioSound : public RefCnt<AudioSound>
{
    friend class AudioEngine;

public:
    bool seek(uint64_t timeInFrames);
    ~AudioSound();
    void stop(uint64_t fadeTimeInFrames = 0);
    float volume();
    void volume(float value);
    bool completed() const;

private:
    AudioSound(AudioEngine* engine,
               rcp<AudioSource> source,
               Artboard* artboard);
    ma_end_clipped_decoder* clippedDecoder() { return &m_decoder; }
    ma_audio_buffer* buffer() { return &m_buffer; }
    ma_sound* sound() { return &m_sound; }
    void dispose();

    ma_end_clipped_decoder m_decoder;
    ma_audio_buffer m_buffer;
    ma_sound m_sound;
    rcp<AudioSource> m_source;

    // This is storage used by the AudioEngine.
    bool m_isDisposed;
    rcp<AudioSound> m_nextPlaying;
    rcp<AudioSound> m_prevPlaying;
    AudioEngine* m_engine;
    Artboard* m_artboard;
};
} // namespace rive

#endif
#endif