/*Copyright (C) 2010-2023 Duncan Deveaux

This file is part of Hikou no mizu.

Hikou no mizu is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Hikou no mizu is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Hikou no mizu.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "SoundBuffer.hpp"

#include "Tools/Log.hpp"
#include "Tools/Format.hpp"
#include "Tools/BuildValues.hpp" ///< Generated at build time
#include <vorbis/vorbisfile.h>
#include <vector>

SoundBuffer::SoundBuffer() : m_alBuffer(0), m_format(0), m_frequency(0)
{

}

SoundBuffer::~SoundBuffer()
{
    alDeleteBuffers(1, &m_alBuffer);
}

//Get and set methods
ALuint SoundBuffer::getALBuffer() const
{
    return m_alBuffer;
}

//Load methods
bool SoundBuffer::loadFromOGG(const std::string &relPath)
{
    //Attempt to open an ogg/vorbis file
    const std::string path = BuildValues::data(relPath);

    OggVorbis_File oggFile;
    if (ov_fopen(path.c_str(), &oggFile) < 0)
    {
        Log::err(Format::format("The audio file: {} could not be opened "
                                "or recognized as a valid vorbis file", path));
        return false;
    }

    //Read info
    const vorbis_info *oggInfos = ov_info(&oggFile, -1);
    const ALsizei frequency = static_cast<ALsizei>(oggInfos->rate);

    ALenum format;
    if (oggInfos->channels == 1)
        format = AL_FORMAT_MONO16;
    else if (oggInfos->channels == 2)
        format = AL_FORMAT_STEREO16;
    else
    {
        Log::err(Format::format("The audio file: {} has an unsupported number ",
                                "of channels ({})", path, oggInfos->channels));
        ov_clear(&oggFile);
        return false;
    }

    //Get samples
    std::vector<char> samples;

    char buffer[4096];
    long bytes = 0;
    while ((bytes = ov_read(&oggFile, buffer, 4096, 0, 2, 1, nullptr)) > 0)
        samples.insert(samples.end(), buffer, buffer + bytes);

    if (samples.empty())
    {
        Log::err(Format::format("Could not load any sample from the "
                                "audio file: {}", path));
        ov_clear(&oggFile);
        return false;
    }

    //Load openAL buffer
    if (!loadFromMemory(&samples[0], static_cast<ALsizei>(samples.size()),
                        format, frequency))
    {
        Log::err(Format::format("The audio file: {} could not be handled "
                                "by OpenAL", path));
        ov_clear(&oggFile);
        return false;
    }

    //Close file
    ov_clear(&oggFile);
    return true;
}

bool SoundBuffer::loadFromMemory(const ALvoid *samples, ALsizei size,
                                 ALenum format, ALsizei frequency)
{
    //Delete previous resources
    alDeleteBuffers(1, &m_alBuffer);

    alGetError(); //Clear errors

    alGenBuffers(1, &m_alBuffer);
    alBufferData(m_alBuffer, format, samples, size, frequency);

    if (alGetError() == AL_NO_ERROR)
    {
        m_format = format;
        m_frequency = frequency;

        return true;
    }

    //Delete broken resources
    alDeleteBuffers(1, &m_alBuffer);
    return false;
}
