#include "manage_hiscore_table.h"
#include <SDL3/SDL_error.h>    // Para SDL_GetError
#include <SDL3/SDL_iostream.h> // Para SDL_ReadIO, SDL_WriteIO, SDL_CloseIO
#include <SDL3/SDL_log.h>      // Para SDL_LogCategory, SDL_LogError, SDL_Lo...
#include <algorithm>           // Para find_if, sort
#include <iterator>            // Para distance
#include "utils.h"             // Para getFileName

// Resetea la tabla a los valores por defecto
void ManageHiScoreTable::clear()
{
    // Limpia la tabla
    table_.clear();

    // Añade 10 entradas predefinidas
    table_.push_back(HiScoreEntry("BRY", 1000000));
    table_.push_back(HiScoreEntry("USU", 500000));
    table_.push_back(HiScoreEntry("FON", 100000));
    table_.push_back(HiScoreEntry("DOG", 50000));
    table_.push_back(HiScoreEntry("LUC", 10000));
    table_.push_back(HiScoreEntry("ASP", 5000));
    table_.push_back(HiScoreEntry("ARR", 1000));
    table_.push_back(HiScoreEntry("ABA", 500));
    table_.push_back(HiScoreEntry("LER", 200));
    table_.push_back(HiScoreEntry("AXX", 100));

    sort();
}

// Añade un elemento a la tabla
int ManageHiScoreTable::add(const HiScoreEntry &entry)
{
    // Añade la entrada a la tabla
    table_.push_back(entry);

    // Ordena la tabla
    sort();

    // Encontrar la posición del nuevo elemento
    auto it = std::find_if(table_.begin(), table_.end(), [&](const HiScoreEntry &e)
                           { return e.name == entry.name &&
                                    e.score == entry.score &&
                                    e.one_credit_complete == entry.one_credit_complete; });

    int position = -1;
    if (it != table_.end())
    {
        position = std::distance(table_.begin(), it);
    }

    // Deja solo las 10 primeras entradas
    if (table_.size() > 10)
    {
        table_.resize(10);

        // Si el nuevo elemento quedó fuera del top 10
        if (position >= 10)
        {
            position = -1; // No entró en el top 10
        }
    }

    // Devuelve la posición
    return position;
}

// Ordena la tabla
void ManageHiScoreTable::sort()
{
    struct
    {
        bool operator()(const HiScoreEntry &a, const HiScoreEntry &b) const { return a.score > b.score; }
    } score_descending_comparator;

    std::sort(table_.begin(), table_.end(), score_descending_comparator);
}

// Carga la tabla desde un fichero
bool ManageHiScoreTable::loadFromFile(const std::string &file_path)
{
    clear();
    auto success = true;
    auto file = SDL_IOFromFile(file_path.c_str(), "rb");

    if (file)
    {
        table_.clear(); // Limpia la tabla actual

        // Lee el número de entradas en la tabla
        int table_size = 0;
        SDL_ReadIO(file, &table_size, sizeof(int));

        // Lee los datos de cada entrada
        for (int i = 0; i < table_size; ++i)
        {
            HiScoreEntry entry;

            // Lee la puntuación
            SDL_ReadIO(file, &entry.score, sizeof(int));

            // Lee el tamaño del nombre y luego el nombre
            int name_size = 0;
            SDL_ReadIO(file, &name_size, sizeof(int));

            std::vector<char> name_buffer(name_size + 1);
            SDL_ReadIO(file, name_buffer.data(), name_size);
            name_buffer[name_size] = '\0'; // Asegurar el fin de la cadena
            entry.name = std::string(name_buffer.data());

            // Lee el valor de one_credit_complete
            int occ_value = 0;
            SDL_ReadIO(file, &occ_value, sizeof(int));
            entry.one_credit_complete = (occ_value != 0);

            // Añade la entrada a la tabla
            table_.push_back(entry);
        }

        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(file_path).c_str());
        SDL_CloseIO(file);
    }
    else
    {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Unable to load %s file! %s", getFileName(file_path).c_str(), SDL_GetError());
        success = false;
    }
    return success;
}

// Guarda la tabla en un fichero
bool ManageHiScoreTable::saveToFile(const std::string &file_path)
{
    auto success = true;
    auto file = SDL_IOFromFile(file_path.c_str(), "w+b");

    if (file)
    {
        // Guarda el número de entradas en la tabla
        int table_size = static_cast<int>(table_.size());
        SDL_WriteIO(file, &table_size, sizeof(int));

        // Guarda los datos de cada entrada
        for (int i = 0; i < table_size; ++i)
        {
            const HiScoreEntry &entry = table_.at(i);

            // Guarda la puntuación
            SDL_WriteIO(file, &entry.score, sizeof(int));

            // Guarda el tamaño del nombre y luego el nombre
            int name_size = static_cast<int>(entry.name.size());
            SDL_WriteIO(file, &name_size, sizeof(int));
            SDL_WriteIO(file, entry.name.c_str(), name_size);

            // Guarda el valor de one_credit_complete como un entero (0 o 1)
            int occ_value = entry.one_credit_complete ? 1 : 0;
            SDL_WriteIO(file, &occ_value, sizeof(int));
        }

        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Writing file: %s", getFileName(file_path).c_str());
        SDL_CloseIO(file);
    }
    else
    {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Unable to save %s file! %s", getFileName(file_path).c_str(), SDL_GetError());
        success = false;
    }
    return success;
}