#pragma once

#include "pxl8_hal.h"
#include "pxl8_types.h"

#define PXL8_SFX_BUFFER_SIZE 1024
#define PXL8_SFX_MAX_CONTEXTS 8
#define PXL8_SFX_MAX_DELAY_SAMPLES 48000
#define PXL8_SFX_MAX_VOICES 16
#define PXL8_SFX_SAMPLE_RATE 48000

typedef struct pxl8_sfx_context pxl8_sfx_context;
typedef struct pxl8_sfx_mixer pxl8_sfx_mixer;
typedef struct pxl8_sfx_node pxl8_sfx_node;

typedef enum pxl8_sfx_filter_type {
    PXL8_SFX_FILTER_BANDPASS = 0,
    PXL8_SFX_FILTER_HIGHPASS,
    PXL8_SFX_FILTER_LOWPASS,
    PXL8_SFX_FILTER_NONE
} pxl8_sfx_filter_type;

typedef enum pxl8_sfx_lfo_target {
    PXL8_SFX_LFO_AMPLITUDE = 0,
    PXL8_SFX_LFO_FILTER,
    PXL8_SFX_LFO_PITCH
} pxl8_sfx_lfo_target;

typedef enum pxl8_sfx_node_type {
    PXL8_SFX_NODE_COMPRESSOR,
    PXL8_SFX_NODE_DELAY,
    PXL8_SFX_NODE_REVERB
} pxl8_sfx_node_type;

typedef enum pxl8_sfx_waveform {
    PXL8_SFX_WAVE_NOISE = 0,
    PXL8_SFX_WAVE_PULSE,
    PXL8_SFX_WAVE_SAW,
    PXL8_SFX_WAVE_SINE,
    PXL8_SFX_WAVE_SQUARE,
    PXL8_SFX_WAVE_TRIANGLE
} pxl8_sfx_waveform;

typedef struct pxl8_sfx_adsr {
    f32 attack;
    f32 decay;
    f32 sustain;
    f32 release;
} pxl8_sfx_adsr;

typedef struct pxl8_sfx_compressor_config {
    f32 attack;
    f32 ratio;
    f32 release;
    f32 threshold;
} pxl8_sfx_compressor_config;

typedef struct pxl8_sfx_delay_config {
    f32 feedback;
    f32 mix;
    u32 time_l;
    u32 time_r;
} pxl8_sfx_delay_config;

typedef struct pxl8_sfx_reverb_config {
    f32 damping;
    f32 mix;
    f32 room;
} pxl8_sfx_reverb_config;

typedef struct pxl8_sfx_voice_params {
    pxl8_sfx_adsr amp_env;
    pxl8_sfx_adsr filter_env;
    pxl8_sfx_filter_type filter_type;
    pxl8_sfx_lfo_target lfo_target;
    pxl8_sfx_waveform lfo_waveform;
    pxl8_sfx_waveform waveform;
    f32 filter_cutoff;
    f32 filter_env_depth;
    f32 filter_resonance;
    f32 fx_send;
    f32 lfo_depth;
    f32 lfo_rate;
    f32 pulse_width;
} pxl8_sfx_voice_params;

#ifdef __cplusplus
extern "C" {
#endif

pxl8_sfx_node* pxl8_sfx_compressor_create(pxl8_sfx_compressor_config cfg);
void pxl8_sfx_compressor_set_attack(pxl8_sfx_node* node, f32 attack);
void pxl8_sfx_compressor_set_ratio(pxl8_sfx_node* node, f32 ratio);
void pxl8_sfx_compressor_set_release(pxl8_sfx_node* node, f32 release);
void pxl8_sfx_compressor_set_threshold(pxl8_sfx_node* node, f32 threshold);

void pxl8_sfx_context_append_node(pxl8_sfx_context* ctx, pxl8_sfx_node* node);
pxl8_sfx_context* pxl8_sfx_context_create(void);
void pxl8_sfx_context_destroy(pxl8_sfx_context* ctx);
pxl8_sfx_node* pxl8_sfx_context_get_head(pxl8_sfx_context* ctx);
f32 pxl8_sfx_context_get_volume(const pxl8_sfx_context* ctx);
void pxl8_sfx_context_insert_node(pxl8_sfx_context* ctx, pxl8_sfx_node* after, pxl8_sfx_node* node);
void pxl8_sfx_context_remove_node(pxl8_sfx_context* ctx, pxl8_sfx_node* node);
void pxl8_sfx_context_set_volume(pxl8_sfx_context* ctx, f32 volume);

pxl8_sfx_node* pxl8_sfx_delay_create(pxl8_sfx_delay_config cfg);
void pxl8_sfx_delay_set_feedback(pxl8_sfx_node* node, f32 feedback);
void pxl8_sfx_delay_set_mix(pxl8_sfx_node* node, f32 mix);
void pxl8_sfx_delay_set_time(pxl8_sfx_node* node, u32 time_l, u32 time_r);

#define PXL8_SFX_EVENT_NOTE_ON  1
#define PXL8_SFX_EVENT_NOTE_OFF 2

typedef void (*pxl8_sfx_event_callback)(u8 event_type, u8 context_id, u8 note, f32 volume, void* userdata);

void pxl8_sfx_mixer_attach(pxl8_sfx_mixer* mixer, pxl8_sfx_context* ctx);
void pxl8_sfx_mixer_clear(pxl8_sfx_mixer* mixer);
pxl8_sfx_mixer* pxl8_sfx_mixer_create(const pxl8_hal* hal);
void pxl8_sfx_mixer_destroy(pxl8_sfx_mixer* mixer);
void pxl8_sfx_mixer_detach(pxl8_sfx_mixer* mixer, pxl8_sfx_context* ctx);
f32 pxl8_sfx_mixer_get_master_volume(const pxl8_sfx_mixer* mixer);
void pxl8_sfx_mixer_process(pxl8_sfx_mixer* mixer);
void pxl8_sfx_mixer_set_event_callback(pxl8_sfx_mixer* mixer, pxl8_sfx_event_callback cb, void* userdata);
void pxl8_sfx_mixer_set_master_volume(pxl8_sfx_mixer* mixer, f32 volume);

void pxl8_sfx_node_destroy(pxl8_sfx_node* node);
f32 pxl8_sfx_note_to_freq(u8 note);
u16 pxl8_sfx_play_note(pxl8_sfx_context* ctx, u8 note, const pxl8_sfx_voice_params* params, f32 volume, f32 duration);
void pxl8_sfx_release_voice(pxl8_sfx_context* ctx, u16 voice_id);

pxl8_sfx_node* pxl8_sfx_reverb_create(pxl8_sfx_reverb_config cfg);
void pxl8_sfx_reverb_set_damping(pxl8_sfx_node* node, f32 damping);
void pxl8_sfx_reverb_set_mix(pxl8_sfx_node* node, f32 mix);
void pxl8_sfx_reverb_set_room(pxl8_sfx_node* node, f32 room);

void pxl8_sfx_stop_all(pxl8_sfx_context* ctx);
void pxl8_sfx_stop_voice(pxl8_sfx_context* ctx, u16 voice_id);

#ifdef __cplusplus
}
#endif
