#include "audio.h"
#include "enginestate.h"
#include <string.h>


void audio_cb(p_audio_buffer *buffer);


void audio_init()
{
	audio_player *player = &engine.audio;

	player->track_count    = 0;
	player->track_capacity = 32;
	player->tracks         = (audio_track *) p_alloc(player->track_capacity * sizeof(audio_track));
	player->next_id        = 0;
	player->sample_rate    = p_audio_sample_rate();

	p_audio_register_data_callback(audio_cb);
}

track_id audio_add_track(p_audio_buffer *data, u32 flags)
{
	// @Correctness: fix track if it has a different sample rate
	audio_player *player = &engine.audio;
	audio_track track;

	track.id = player->next_id;
	player->next_id++;
	track.data = *data;
	track.progress = 0;
	track.playing = !(flags & AUDIO_PAUSED);
	track.loop = !!(flags & AUDIO_LOOP);
	track.free_on_finish = !!(flags & AUDIO_FREE_ON_FINISH);
	track.volume = 1.0;

	if (player->track_count + 1 < player->track_capacity)
	{
		u64 index = player->track_count;
		player->tracks[index] = track;
		player->track_count++;
	}
	else
	{
		u32 replace_index = 0;
		for(u32 i = 0; i < player->track_count; i++)
		{
			audio_track *current = &player->tracks[i];
			if(track.loop == current->loop)
			{
				replace_index = i;
				break;
			}
		}

		player->tracks[replace_index] = track;
	}

	return track.id;
}

void audio_remove_track(track_id id)
{
	audio_player *player = &engine.audio;
	for(int i = 0; i < player->track_count; i++)
	{
		audio_track *current = &player->tracks[i];
		if(id == current->id)
		{
			u32 move_count = player->track_count - (i + 1);
			if(current->free_on_finish)
				p_free(current->data.samples);
			memmove(current, current + 1, move_count * sizeof(audio_track));
			player->track_count--;
			break;
		}
	}
}

void audio_pause_track(track_id id)
{
	audio_track *track = audio_track_from_id(id);
	if(track)
		track->playing = false;
}

void audio_play_track(track_id id)
{
	audio_track *track = audio_track_from_id(id);
	if(track)
		track->playing = true;
}

void audio_change_track_volume(track_id id, f32 volume)
{
	audio_track *track = audio_track_from_id(id);
	if(track)
		track->volume = volume;
}

audio_track *audio_track_from_id(track_id id)
{
	audio_player *player = &engine.audio;
	for(int i = 0; i < player->track_count; i++)
	{
		audio_track *current = &player->tracks[i];
		if(id == current->id)
			return current;
	}
	return NULL;
}


// @Correctness: audio cb modifies the array of tracks asynchronously. All r/w operations on tracks should be protected by a mutex.
void audio_cb(p_audio_buffer *buffer)
{
	audio_player *player = &engine.audio;

	buffer->size = minimum(buffer->size, 2048); // Low latency

	for(u64 i = 0; i < buffer->size; i++)
	{
		buffer->samples[i].left  = 0;
		buffer->samples[i].right = 0;
	}

	for(int track_i = 0; track_i < player->track_count; track_i++)
	{
		audio_track *current = &player->tracks[track_i];
		if(current->playing)
		{
			u64 remaining = current->data.size - current->progress;
			u64 copy_count = minimum(remaining, buffer->size);
			if(current->loop)
				copy_count = buffer->size;

			for(u64 i = 0; i < copy_count; i++)
			{
				buffer->samples[i].left  += current->volume * current->data.samples[current->progress].left;
				buffer->samples[i].right += current->volume * current->data.samples[current->progress].right;
				current->progress = (current->progress + 1) % current->data.size;
			}

			if(!current->loop && current->progress == 0) // current->progress == 0 because we finished and then we looped around
			{
				// Finished playing. Remove track
				u32 move_count = player->track_count - (track_i + 1);
				if(current->free_on_finish)
					p_free(current->data.samples); // @Bug: double free when the same sound is played 2 times. Find a way to signal we reached the end of the track so the user code can make its own free (if needed)
				memmove(current, current + 1, move_count * sizeof(audio_track));
				track_i--;
				player->track_count--;
			}
		}
	}
}
