/* This file is part of Korman.
 *
 * Korman 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.
 *
 * Korman 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 Korman.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "sound.h"
#include "PyHSPlasma_private.h"

#include <PRP/Audio/plSoundBuffer.h>
#include <Stream/hsStream.h>
#include <vorbis/vorbisfile.h>

static const int BITS_PER_SAMPLE = 16;

static size_t _read_stream(void* ptr, size_t size, size_t nmemb, void* datasource) {
    hsStream* s = static_cast<hsStream*>(datasource);
    // hsStream is a bit overzealous protecting us against overreads, so we need to
    // make sure to not tick it off.
    size_t request = nmemb * size;
    size_t remaining = s->size() - s->pos();
    size_t min = std::min(request, remaining);
    if (min == 0) {
        return 0;
    } else {
        size_t read = s->read(min, ptr);
        return read / size;
    }
}

static int _seek_stream(void* datasource, ogg_int64_t offset, int whence) {
    hsStream* s = static_cast<hsStream*>(datasource);
    switch (whence) {
    case SEEK_CUR:
        s->skip(offset);
        break;
    case SEEK_SET:
        s->seek(offset);
        break;
    case SEEK_END:
        s->seek(s->size() - offset);
        break;
    }
    return 0;
}

static long _tell_stream(void* datasource) {
    hsStream* s = static_cast<hsStream*>(datasource);
    return s->pos();
}

static ov_callbacks s_callbacks = {
    (size_t(*)(void *, size_t, size_t, void *))_read_stream,
    (int (*)(void *, ogg_int64_t, int))_seek_stream,
    (int (*)(void *))NULL,
    (long (*)(void *))_tell_stream,
};

PyObject* inspect_vorbisfile(PyObject*, PyObject* args) {
    pyStream* stream;
    pyWAVHeader* header;
    if (!PyArg_ParseTuple(args, "OO", &stream, &header)) {
        PyErr_SetString(PyExc_TypeError, "inspect_vorbisfile expects an hsStream, plWAVHeader");
        return NULL;
    }

    // The OGG file may actually be in Blender's memory, so we will use hsStream. Therefore,
    // we must tell vorbisfile how to do this.
    OggVorbis_File vorbis;
    int result = ov_open_callbacks(stream->fThis, &vorbis, NULL, 0, s_callbacks);
    if (result < 0) {
        PyErr_Format(PyExc_RuntimeError, "vorbisfile ov_open_callbacks: %d", result);
        return NULL;
    }

    header->fThis->setFormatTag(plWAVHeader::kPCMFormatTag);
    header->fThis->setBitsPerSample(BITS_PER_SAMPLE);
    vorbis_info* info = ov_info(&vorbis, -1);
    header->fThis->setNumChannels(info->channels);
    header->fThis->setNumSamplesPerSec(info->rate);
    unsigned short align = (BITS_PER_SAMPLE * info->channels) >> 3;
    header->fThis->setBlockAlign(align);
    header->fThis->setAvgBytesPerSec(info->rate * align);

    // This behavior was copied and pasted from CWE
    ogg_int64_t size = (ov_pcm_total(&vorbis, -1) - 1) * align;

    // Cleanup
    ov_clear(&vorbis);

    // We got the plWAVHeader from Python because we don't link against PyHSPlasma, only libHSPlasma
    // Therefore, we only need to return the size.
    return PyLong_FromSsize_t(size);
}
