#include "sound/soundmanager.h"
#include <AL/al.h>
#include <glm/gtc/type_ptr.hpp>

SoundManager::SoundManager()
{
	for (auto& s : sources) {
		alGenSources(1, &s.source);
		s.inUse = false;
		s.priority = 0;
		alSourcef(s.source, AL_REFERENCE_DISTANCE, 1.0f);
		alSourcef(s.source, AL_MAX_DISTANCE, 10000.f);
		alSourcef(s.source, AL_ROLLOFF_FACTOR, 0.3f);
	}
	alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
}

void SoundManager::playSound(ALuint buffer, int priority, const glm::vec3& pos)
{
	std::size_t ring = lastUsed;
	AudioSource* nextBest = nullptr;
	std::size_t secIndex = 0;
	auto nextIndex = [](std::size_t i){ return (i + 1) % 10; };

	if (!sources[ring].inUse) {
		sources[ring].inUse = true;
		sources[ring].priority = priority;
		sources[ring].buffer = buffer;
		alSourcei(sources[ring].source, AL_BUFFER, buffer);
		if (pos != glm::vec3(0)) {
			//alSourcei(sources[ring].source, AL_SOURCE_RELATIVE, 1);
			alSourcefv(sources[ring].source, AL_POSITION, glm::value_ptr(pos));
		} else {
			//alSourcei(sources[ring].source, AL_SOURCE_RELATIVE, 0);
		}
		alSourcePlay(sources[ring].source);
		lastUsed = nextIndex(lastUsed);
		return;
	}
	for (ring = nextIndex(lastUsed); ring != lastUsed; ring = nextIndex(ring)) {
		if (!sources[ring].inUse) {
			sources[ring].inUse = true;
			sources[ring].priority = priority;
			sources[ring].buffer = buffer;
			alSourcei(sources[ring].source, AL_BUFFER, buffer);
			if (pos != glm::vec3(0)) {
				//alSourcei(sources[ring].source, AL_SOURCE_RELATIVE, 1);
				alSourcefv(sources[ring].source, AL_POSITION, glm::value_ptr(pos));
			} else {
				//alSourcei(sources[ring].source, AL_SOURCE_RELATIVE, 0);
			}
			alSourcePlay(sources[ring].source);
			lastUsed = nextIndex(ring);
			return;
		}
		if (sources[ring].priority > priority || (sources[ring].buffer != buffer && sources[ring].priority == priority)) {
			if (nextBest) {
				if (nextBest->priority >= sources[ring].priority) {
					nextBest = &sources[ring];
					secIndex = ring;
				}
			} else {
				nextBest = &sources[ring];
				secIndex = ring;
			}
		}
	}
	if (nextBest) {
		nextBest->priority = priority;
		alSourcei(nextBest->source, AL_BUFFER, 0);
		alSourcei(nextBest->source, AL_BUFFER, buffer);
		nextBest->buffer = buffer;
		if (pos != glm::vec3(0)) {
			//alSourcei(nextBest->source, AL_SOURCE_RELATIVE, 1);
			alSourcefv(nextBest->source, AL_POSITION, glm::value_ptr(pos));
		} else {
			//alSourcei(nextBest->source, AL_SOURCE_RELATIVE, 0);
		}
		alSourcePlay(nextBest->source);
		lastUsed = secIndex;
	}
}

void SoundManager::pollSources()
{
	for (auto& s : sources) {
		if (s.inUse) {
			ALint processed, state;
			alGetSourcei(s.source, AL_BUFFERS_PROCESSED, &processed);
			alGetSourcei(s.source, AL_SOURCE_STATE, &state);
			if (state == AL_STOPPED) {
				s.inUse = false;
				s.buffer = 0;
				alSourcei(s.source, AL_BUFFER, 0);
			}
			//else if (state != AL_PLAYING)
			//	alSourcePlay(s.source);
		}
	}
}

SoundManager::~SoundManager()
{
	for (auto& s : sources) {
		alSourcei(s.source, AL_BUFFER, 0);
		alDeleteSources(1, &s.source);
	}
}
