#include "pch.h"
#include "AudioMixer.h"

#include "engine/Camera.h"

int64_t ReverbEffect::initialize() {
	ma_reverb_node_config reverb_node_config = ma_reverb_node_config_init(2,this->engine->sampleRate);
	// ma_reverb_node_config reverb_node_config = ma_reverb_node_config_init(2,this->engine->sampleRate);
	// ma_result result = ma_reverb_node_init(ma_engine_get_node_graph(engine), &reverb_node_config, NULL, &this->reverb_node);
	// ma_result result = ma_reverb_node_init(&WEngine->audio_mixer->node_graph, &reverb_node_config, NULL, &this->reverb_node);
	// ma_result result = ma_reverb_node_init(&WEngine->audio_mixer->engine.nodeGraph, &reverb_node_config, NULL, &this->reverb_node);
	ma_result result = ma_reverb_node_init(&WEngine->audio_mixer->engine.nodeGraph, &reverb_node_config, NULL, &this->reverb_node);
	if(result != MA_SUCCESS) {
		std::cout << "Failed to init reverb \n";
	}
	initialized = true;
	return result;
}

ReverbEffect::ReverbEffect(ma_engine* engine, ma_device* device): AudioEffect(engine, device) {
	// reverb_node = new ma_reverb_node();
	this->initialize();
	// shift_node = new ma_pitch_shift_node;

}

ma_node* ReverbEffect::get_node() {
	return (ma_node*)&reverb_node;
	// reverb_node->reverb.
}

int64_t ReverbEffect::connect_to_node(ma_node* node) {
	ma_result res = ma_node_attach_output_bus(&this->reverb_node, 0, node, 0);
	return res;
}

AudioFile::AudioFile(ma_engine* engine, std::string_view name, AudioFileDescriptor desc ): engine(engine) {	
	ma_sound_group* sound_group = desc.sound_group.has_value() ? desc.sound_group.value()->sound_group : WEngine->audio_mixer->default_sound_group.sound_group;
	bool spatialization_enabled = desc.spatialization_enabled;

	wlog_info("{} {}", "Init audio file:", name);

	// ma_reverb
	for (int i = 0; i < PRELOAD_SOUNDS_CNT; i++) {
		this->sounds[i] = ma_sound();

		ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(ma_engine_get_channels(&WEngine->audio_mixer->engine));
		if(MA_SUCCESS != ma_splitter_node_init(WEngine->audio_mixer->node_graph, &splitterNodeConfig, NULL, &this->splitter_nodes[i])) {
			std::cout << "can't connect ma splitter node";
		}
		ma_node_attach_output_bus(&this->splitter_nodes[i], 0, ma_node_graph_get_endpoint(WEngine->audio_mixer->node_graph), 0);
		ma_node_attach_output_bus(&this->splitter_nodes[i], 1, WEngine->audio_mixer->reverb_effect->get_node(), 0);
	}
	ma_sound_init_from_file(engine, &name[0], 0, sound_group, NULL, &this->sounds[0]);

	for (int i = 1; i < PRELOAD_SOUNDS_CNT; i++) {
		ma_sound_init_copy(engine, &this->sounds[0], 0, sound_group, &this->sounds[i]);
	}
	if(!spatialization_enabled) {
		for (int i = 0; i < PRELOAD_SOUNDS_CNT; i++) {
			ma_sound_set_spatialization_enabled(&this->sounds[i], false);
		}
	} else {
		for (int i = 0; i < PRELOAD_SOUNDS_CNT; i++) {
			ma_node_attach_output_bus(&this->sounds[i], 0, &this->splitter_nodes[i], 0);
			// ma_node_attach_output_bus(&this->sounds[i], 0, ma_node_graph_get_endpoint(WEngine->audio_mixer->node_graph), 0);
			// ma_node_attach_output_bus(&this->sounds[i], 1, WEngine->audio_mixer->reverb_effect->get_node(), 0);
			// ma_node_attach_output_bus(&this->sounds[i], 0, WEngine->audio_mixer->node_graph, 0);
		}
	}
	// ma_sound_seek_to_pcm_frame(&this->sounds[k],uint64_t(t * float(this->engine->sampleRate)))
	ma_sound_get_length_in_seconds(&this->sounds[0],&this->length_seconds);
}

bool AudioFile::is_playing(int k) {
	return !!ma_sound_is_playing(&sounds[k]);
}

// int AudioFile::start(int k, float vol, std::optional<glm::vec3> pos) {
float AudioFile::get_current_time() {
	float sound_cursor = 0;
	if(this->is_playing()) {
		ma_sound_get_cursor_in_seconds(&this->sounds[0], &sound_cursor);
	}
	return sound_cursor;
}


void AudioFile::set_current_time(float t, int k) {
	ma_sound_seek_to_pcm_frame(&this->sounds[k],uint64_t(t * float(this->engine->sampleRate)));
	// if(this->is_playing(k)) {
	// this->stop(k);
	// }
	// this->start(AudioPlayDescriptor{.k = k, .start_offset = t});
	;
	

	// ma_sound_get_cursor_in_pcm_frames()
	// float sound_cursor = 0;
	// if(this->is_playing()) {
	// 	ma_sound_get_cursor_in_seconds(&this->sounds[0], &sound_cursor);
	// 	ma_sound
	// 	// ma_engine_set_time()
	// }
	// return sound_cursor;
}

int AudioFile::start(AudioPlayDescriptor desc) {
	int k = desc.k;
	std::optional<glm::vec3> pos = desc.pos;
	float vol = desc.vol;
	if (k == -1) {
		// ma_sound* sound = &this->sounds[file_idx++];
		// ma_sound_start(sound);
		for (int i = 0; i < PRELOAD_SOUNDS_CNT; i++) {
			ma_sound* sound = &this->sounds[i];
			if (!ma_sound_is_playing(sound)) {
				if (vol >= 0.0f) {
					ma_sound_set_volume(sound, vol);
				} else {
					ma_sound_set_volume(sound, this->vol);
				}
				// ma_sound_set_start_time_in_milliseconds(sound,uint64_t(desc.start_offset*1000));
				if(pos.has_value()) {
					glm::vec3 _pos = pos.value();
					// ma_sound_set_position(&g_sound, (float)x * distance, 0, (float)y * distance);
					ma_sound_set_position(sound, _pos.x, _pos.y, _pos.z);
					// ma_node_set_output_bus_volume(WEngine->camera->pos)
					float interp_fac = glm::smoothstep(0.0f,10.0f, glm::length(pos.value() - WEngine->camera->pos));
					// WEngine->editor->send_notification_message(std::to_string(interp_fac));
					ma_node_set_output_bus_volume(&this->splitter_nodes[i], 1, interp_fac);
					// ma_node_set_output_bus_volume(&this->splitter_nodes[i], 1, 0.0f);
				}
				ma_sound_start(sound);
				return i;
			}
		}
	} else {
		ma_sound* sound = &this->sounds[k];
		// ma_sound_set_start_time_in_milliseconds(sound,uint64_t(desc.start_offset*1000));
		if (ma_sound_is_playing(sound)) {
			ma_sound_stop(sound);
			ma_sound_start(sound);
		} else {
			ma_sound_start(sound);
		}
		return k;
	}
	return 0;
}

int AudioFile::stop(int k) {
	if (k == -1) {
		for (int i = 0; i < PRELOAD_SOUNDS_CNT; i++) {
			ma_sound* sound = &this->sounds[i];
			if (ma_sound_is_playing(sound)) {
				ma_sound_stop(sound);
			}
		}
	} else {
		ma_sound_stop(&this->sounds[k]);
	}
	return k;
}

void AudioFile::set_volume(float linear_volume) {
	this->vol = linear_volume;
	for (int i = 0; i < PRELOAD_SOUNDS_CNT; i++) {
		ma_sound* sound = &this->sounds[i];
		ma_sound_set_volume(sound, this->vol);
	}
}

void AudioFile::set_looping_enabled(bool looping) {
	this->looping = looping;
	for (int i = 0; i < PRELOAD_SOUNDS_CNT; i++) {
		ma_sound* sound = &this->sounds[i];
		ma_sound_set_looping(sound, (ma_bool32)this->looping);
	}
}

uint64_t AudioFile::connect_effect(AudioEffect* m_effect) {
	return 0;
	// return ma_node_attach_output_bus(&this->sound, 0, m_effect->get_node(), 0);
}

AudioMixer::AudioMixer() {
	WEngine->audio_mixer = this;

	ma_engine_config engineConfig;
	engineConfig = ma_engine_config_init();
	engineConfig.channels           = 2;
	engineConfig.sampleRate         = 44100;
	engineConfig.periodSizeInFrames = 256;
	engineConfig.listenerCount = 1;
	// ma_result result;
	ma_engine_init(&engineConfig, &this->engine);

	this->node_graph = &this->engine.nodeGraph;

	this->default_sound_group = SoundGroup(&this->engine, nullptr);
	this->sfx_sound_group = SoundGroup(&this->engine, nullptr);




	// ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(device.capture.channels);
	// ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(2);
	// auto result = ma_node_graph_init(&nodeGraphConfig, NULL, &this->node_graph);
	// this.nod

	// ma_node_graph* node_graph = ma_engine_get_node_graph(&this->engine);


	// if (result != MA_SUCCESS) {
	// 	printf("Failed to initialize node graph. \n");
	// }

	reverb_effect = new ReverbEffect(&this->engine, &this->device);

	ma_node* endpoint = ma_node_graph_get_endpoint(this->node_graph);
	reverb_effect->connect_to_node(endpoint);


	// ma_audio_buffer_ref_init()

	// IPLAudioSettings iplAudioSettings = {
	// 	0,0
	// };
	//
	// IPLContextSettings iplContextSettings;
	// IPLContext iplContext;
	// IPLHRTFSettings iplHRTFSettings;
	// IPLHRTF iplHRTF;
	//
	// // MA_ZERO_OBJECT(&iplAudioSettings);
	//
	// iplAudioSettings.samplingRate = ma_engine_get_sample_rate(&this->engine);
	// iplAudioSettings.frameSize = engineConfig.periodSizeInFrames;
	//
	//
	// /* IPLContext */
	// #define ZERO_OBJECT(p) memset(&p, 0, sizeof(p));
	// ZERO_OBJECT(iplContextSettings);
	// // memset(&iplContextSettings, 0, sizeof(iplContextSettings));
	// iplContextSettings.version = STEAMAUDIO_VERSION;
	//
	// iplContextCreate(&iplContextSettings, &iplContext);
	// // ---- TO ERR CHECK
	//
	//
	// /* IPLHRTF */
	// ZERO_OBJECT(iplHRTFSettings);
	// iplHRTFSettings.type = IPL_HRTFTYPE_DEFAULT;
	//
	// iplHRTFCreate(iplContext, &iplAudioSettings, &iplHRTFSettings, &iplHRTF);
	// ma_sound_set_direction()


	// memset(p, 0, sz);
	// ma_zero_memory_default(iplAudioSettings, sizeof(IPLAudioSettings));


	// ma_engine_play_sound(&this->engine, "demoAqqqq.wav", NULL);
	ma_engine_listener_set_world_up(&engine, 0, 0, 1, 0);

}

void AudioMixer::update_player(glm::vec3 pos, glm::vec3 dir, glm::vec3 vel) {

	// ma_sound_set_position(sound, _pos.x, _pos.y, _pos.z);
	ma_engine_listener_set_velocity(&this->engine,0,vel.x, vel.y, vel.z);
	ma_engine_listener_set_position(&this->engine,0,pos.x, pos.y, pos.z);
	ma_engine_listener_set_direction(&this->engine,0,dir.x, dir.y, dir.z);
	// ma_engine_listener_set_cone(&engine, listenerIndex, innerAngleInRadians, outerAngleInRadians, outerGain);
}

void AudioMixer::set_master_volume(float vol) {
	ma_engine_set_volume(&this->engine, this->master_vol = vol);
}


SoundGroup::SoundGroup(ma_engine* engine, ma_sound_group* parent_group) {
	// sound_group = ma_sound_group();
	this->sound_group = new ma_sound_group;
	// sound_group = &sound_group_storage;
	ma_sound_group_init(engine, 0, parent_group, this->sound_group);
}

SoundGroup::SoundGroup(ma_sound_group* _sound_group): name(name) {
	// sound_group_storage = *_sound_group;
	this->sound_group = _sound_group;
}

void SoundGroup::set_volume(float linear_volume) {
	ma_sound_group_set_volume(this->sound_group, this->vol = linear_volume);
}
