/**
 * @file driver.h
 * @brief Audio driver (audren wrapper).
 * @author fincs
 * @copyright libnx Authors
 */
#pragma once
#include "../services/audren.h"

typedef struct AudioDriverEtc AudioDriverEtc;

typedef struct {
    AudioDriverEtc* etc;
    AudioRendererConfig config;
    AudioRendererMemPoolInfoIn* in_mempools;
    AudioRendererChannelInfoIn* in_channels;
    AudioRendererVoiceInfoIn* in_voices;
    AudioRendererMixInfoIn* in_mixes;
    AudioRendererSinkInfoIn* in_sinks;
} AudioDriver;

Result audrvCreate(AudioDriver* d, const AudioRendererConfig* config, int num_final_mix_channels);
Result audrvUpdate(AudioDriver* d);
void audrvClose(AudioDriver* d);

//-----------------------------------------------------------------------------

int audrvMemPoolAdd(AudioDriver* d, void* buffer, size_t size);
bool audrvMemPoolRemove(AudioDriver* d, int id);
bool audrvMemPoolAttach(AudioDriver* d, int id);
bool audrvMemPoolDetach(AudioDriver* d, int id);

//-----------------------------------------------------------------------------

typedef enum {
    AudioDriverWaveBufState_Free,
    AudioDriverWaveBufState_Waiting,
    AudioDriverWaveBufState_Queued,
    AudioDriverWaveBufState_Playing,
    AudioDriverWaveBufState_Done,
} AudioDriverWaveBufState;

typedef struct AudioDriverWaveBuf AudioDriverWaveBuf;

struct AudioDriverWaveBuf {
    union {
        s16*        data_pcm16;
        u8*         data_adpcm;
        const void* data_raw;
    };
    u64 size;
    s32 start_sample_offset;
    s32 end_sample_offset;
    const void* context_addr;
    u64 context_sz;
    AudioDriverWaveBufState state : 8;
    bool is_looping;
    u32 sequence_id;
    AudioDriverWaveBuf* next;
};

bool audrvVoiceInit(AudioDriver* d, int id, int num_channels, PcmFormat format, int sample_rate);
void audrvVoiceDrop(AudioDriver* d, int id);
void audrvVoiceStop(AudioDriver* d, int id);
bool audrvVoiceIsPaused(AudioDriver* d, int id);
bool audrvVoiceIsPlaying(AudioDriver* d, int id);
bool audrvVoiceAddWaveBuf(AudioDriver* d, int id, AudioDriverWaveBuf* wavebuf);
u32 audrvVoiceGetWaveBufSeq(AudioDriver* d, int id);
u32 audrvVoiceGetPlayedSampleCount(AudioDriver* d, int id);
u32 audrvVoiceGetVoiceDropsCount(AudioDriver* d, int id);
void audrvVoiceSetBiquadFilter(AudioDriver* d, int id, int biquad_id, float a0, float a1, float a2, float b0, float b1, float b2);

static inline void audrvVoiceSetExtraParams(AudioDriver* d, int id, const void* params, size_t params_size)
{
    d->in_voices[id].extra_params_ptr = params;
    d->in_voices[id].extra_params_sz = params_size;
}

static inline void audrvVoiceSetDestinationMix(AudioDriver* d, int id, int mix_id)
{
    d->in_voices[id].dest_mix_id = mix_id;
    d->in_voices[id].dest_splitter_id = AUDREN_UNUSED_SPLITTER_ID;
}

static inline void audrvVoiceSetMixFactor(AudioDriver* d, int id, float factor, int src_channel_id, int dest_channel_id)
{
    int channel_id = d->in_voices[id].channel_ids[src_channel_id];
    d->in_channels[channel_id].mix[dest_channel_id] = factor;
}

static inline void audrvVoiceSetVolume(AudioDriver* d, int id, float volume)
{
    d->in_voices[id].volume = volume;
}

static inline void audrvVoiceSetPitch(AudioDriver* d, int id, float pitch)
{
    d->in_voices[id].pitch = pitch;
}

static inline void audrvVoiceSetPriority(AudioDriver* d, int id, int priority)
{
    d->in_voices[id].priority = priority;
}

static inline void audrvVoiceClearBiquadFilter(AudioDriver* d, int id, int biquad_id)
{
    d->in_voices[id].biquads[biquad_id].enable = false;
}

static inline void audrvVoiceSetPaused(AudioDriver* d, int id, bool paused)
{
    d->in_voices[id].state = paused ? AudioRendererVoicePlayState_Paused : AudioRendererVoicePlayState_Started;
}

static inline void audrvVoiceStart(AudioDriver* d, int id)
{
    audrvVoiceSetPaused(d, id, false);
}

//-----------------------------------------------------------------------------

int audrvMixAdd(AudioDriver* d, int sample_rate, int num_channels);
void audrvMixRemove(AudioDriver* d, int id);

static inline void audrvMixSetDestinationMix(AudioDriver* d, int id, int mix_id)
{
    d->in_mixes[id].dest_mix_id = mix_id;
    d->in_mixes[id].dest_splitter_id = AUDREN_UNUSED_SPLITTER_ID;
}

static inline void audrvMixSetMixFactor(AudioDriver* d, int id, float factor, int src_channel_id, int dest_channel_id)
{
    d->in_mixes[id].mix[src_channel_id][dest_channel_id] = factor;
}

static inline void audrvMixSetVolume(AudioDriver* d, int id, float volume)
{
    d->in_mixes[id].volume = volume;
}

//-----------------------------------------------------------------------------

int audrvDeviceSinkAdd(AudioDriver* d, const char* device_name, int num_channels, const u8* channel_ids);
void audrvSinkRemove(AudioDriver* d, int id);