#include <AL/al.h>
#include <AudioSource/AudioSource.hpp>
#include <Utilities/Utilities.hpp>

std::vector<AudioSource> AudioSource::pool;

AudioSource::AudioSource(bool looping) : currentSound(&Sound::NONE), looping(looping)
{
    alGenSources(1, &source);
    alSourcef(source, AL_PITCH, 1);
    alSourcef(source, AL_GAIN, 1.0f);
    alSource3f(source, AL_POSITION, 0, 0, 0);
    alSource3f(source, AL_VELOCITY, 0, 0, 0);
    alSourcei(source, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
}

AudioSource::AudioSource(AudioSource &&other)
{
    source = other.source;
    currentSound = other.currentSound;
    looping = other.looping;

    other.source = 0;
    other.currentSound = nullptr;
}

AudioSource::~AudioSource()
{
    if (source != 0)
    {
        alDeleteSources(1, &source);
    }
}

void AudioSource::setSound(Sound &sound)
{
    if (looping && &sound == currentSound)
    {
        return;
    }

    if (isPositioned && sound.channels == 2)
    {
        crash("Attempted to play a stereo sound with a positioned audio source!");
    }

    currentSound = &sound;

    alSourceStop(source);
    if (sound.buffer != 0)
    {
        alSourcef(source, AL_ROLLOFF_FACTOR, isPositioned ? 0.5f : 0.0f);
        alSourcei(source, AL_SOURCE_RELATIVE, isPositioned ? AL_FALSE : AL_TRUE);
        alSourcei(source, AL_BUFFER, sound.buffer);
        alSourcePlay(source);
    }
}

void AudioSource::setPosition(glm::vec3 pos)
{
    isPositioned = true;
    alSourcefv(source, AL_POSITION, &pos[0]);
}

void AudioSource::setVelocity(glm::vec3 vel)
{
    alSourcefv(source, AL_VELOCITY, &vel[0]);
}

void AudioSource::setPitch(float pitch)
{
    alSourcef(source, AL_PITCH, pitch);
}

void AudioSource::setGain(float gain)
{
    alSourcef(source, AL_GAIN, gain);
}

bool AudioSource::isPlaying()
{
    ALint state;
    alGetSourcei(source, AL_SOURCE_STATE, &state);
    return state == AL_PLAYING;
}

AudioSource& AudioSource::borrowFromPool()
{
    for (AudioSource &source : pool)
    {
        if (!source.isPlaying())
        {
            source.setPosition(glm::vec3(0.0f));
            source.setVelocity(glm::vec3(0.0f));
            source.setPitch(1.0f);
            source.setGain(1.0f);
            source.isPositioned = false;
            return source;
        }
    }

    pool.push_back(AudioSource(false));
    return pool.back();
}