#include "vgmstream.hpp"
extern "C" {
#include <vgmstream.h>
#include <util.h>
#include <plugins.h>
}
#include <filesystem>
#include "file_backend.hpp"
#include <stddef.h>
#include <string.h>
#include <license.hpp>
#include <assets/assets.h>
void VgmStreamBackend::load(const char *filename) {
    spec.format = AUDIO_S16SYS;
    file = open_file(filename);
    sf = get_sf_from_file(file);
    if (!sf) {
        throw std::exception();
    }
    auto *output = init_vgmstream_from_STREAMFILE(sf);
    if (!output) {
        throw std::exception();
    }
    int stream_count = output->num_streams;
    close_vgmstream(output);
    streams.clear();
    for (int i = 0; i < stream_count; i++) {
        PlaybackStream stream;
        stream.id = i;
        stream.length = 0;
        stream.name = "";
        if (!sf) {
            streams.push_back(stream);
            continue;
        }
        sf->stream_index = i;
        auto *tmp = init_vgmstream_from_STREAMFILE(sf);
        if (!tmp) {
            streams.push_back(stream);
            continue;
        }
        reset_vgmstream(tmp);
        stream.length = (double)tmp->num_samples / (double)tmp->sample_rate;
        char *buf = (char*)calloc(STREAM_NAME_SIZE + 1, 1);
        strncpy(buf, tmp->stream_name, STREAM_NAME_SIZE);
        if (buf[0] == '\0') {
            free(buf);
            buf = strdup("Unknown");
        }
        if (i == 0) {
            char *buf2 = NULL;
            const char *const DEFAULT_FORMAT_STR = "Default (%s)";
            size_t buflen = snprintf(NULL, 0, DEFAULT_FORMAT_STR, buf) + 1;
            buf2 = (char*)calloc(buflen, 1);
            snprintf(buf2, buflen, DEFAULT_FORMAT_STR, buf);
            stream.name = buf2;
            free(buf2);
        } else {
            stream.name = buf;
        }
        streams.push_back(stream);
        free(buf);
        close_vgmstream(tmp);
    }
    switch_stream(0);
}
void VgmStreamBackend::switch_stream(int idx) {
    if (vf == nullptr || sf == nullptr || file == nullptr) {
        open = false;
        return;
    }
    sf->stream_index = idx;
    if (idx >= streams.size()) {
        throw std::exception();
    }
    vf = init_vgmstream_from_STREAMFILE(sf);
    if (!vf) {
        throw std::exception();
    }
    vgmstream_cfg_t cfg = {0};
    cfg.allow_play_forever = 1;
    cfg.play_forever = 1;
    vgmstream_apply_config(vf, &cfg);
    spec.channels = vf->channels;
    spec.freq = vf->sample_rate;
    length = streams[idx].length;
    const char *title_tag = vf->stream_name;
    if (title_tag[0] == '\0') {
        std::filesystem::path path(current_file);
        current_title = path.stem().string();
    } else {
        char *buf = (char*)calloc(STREAM_NAME_SIZE + 1, 1);
        strncpy(buf, title_tag, STREAM_NAME_SIZE);
        current_title = buf;
        free(buf);
    }
    open = true;
}
void VgmStreamBackend::cleanup() {
    streams.clear();
    close_vgmstream(vf);
    close_streamfile(sf);
    vf = nullptr;
    sf = nullptr;
    delete file;
    file = nullptr;
    open = false;
}
size_t VgmStreamBackend::render(void *buf, size_t maxlen) {
    if (vf == nullptr || sf == nullptr || file == nullptr) {
        open = false;
        return 0;
    }
    maxlen /= sizeof(sample_t);
    size_t new_samples = render_vgmstream((sample_t*)(buf), (int)maxlen, vf);
    if (maxlen > new_samples) {
        reset_vgmstream(vf);
        position = 0;
    } else {
        position += new_samples;
    }
    return new_samples * sizeof(sample_t);
}
void VgmStreamBackend::seek_samples(uint64_t position) {

    if (vf == nullptr || sf == nullptr || file == nullptr) {
        open = false;
        return;
    }
    auto pos32 = (int32_t)(position);
    seek_vgmstream(vf, pos32);
    this->position = position;
}
uint64_t VgmStreamBackend::get_position_samples() {
    return position;
}
int VgmStreamBackend::get_stream_idx() {
    return vf->stream_index;
}
void VgmStreamBackend::add_licenses() {
	auto &license_data = get_license_data();
	auto vgmstream = LicenseData("VGMSTREAM", "ISC");
	LOAD_LICENSE(vgmstream, vgmstream);
	license_data.insert(vgmstream);
}