#include "manage_hiscore_table.h"
#include <SDL2/SDL_error.h> // for SDL_GetError
#include <SDL2/SDL_rwops.h> // for SDL_RWread, SDL_RWwrite, SDL_RWFromFile
#include <stdlib.h>         // for free, malloc
#include <algorithm>        // for sort
#include <iostream>         // for basic_ostream, char_traits, operator<<
#include "utils.h"          // for HiScoreEntry

// Constructor
ManageHiScoreTable::ManageHiScoreTable(std::vector<HiScoreEntry> *table)
    : table(table) {}

// Resetea la tabla a los valores por defecto
void ManageHiScoreTable::clear()
{
    // Limpia la tabla
    table->clear();

    // Añade 10 entradas predefinidas
    table->push_back({"Bry", 1000000});
    table->push_back({"Usufondo", 500000});
    table->push_back({"G.Lucas", 100000});
    table->push_back({"P.Delgat", 50000});
    table->push_back({"P.Arrabalera", 10000});
    table->push_back({"Pelechano", 5000});
    table->push_back({"Sahuquillo", 1000});
    table->push_back({"Bacteriol", 500});
    table->push_back({"Pepe", 200});
    table->push_back({"Rosita", 100});
}

// Añade un elemento a la tabla
void ManageHiScoreTable::add(HiScoreEntry entry)
{
    // Añade la entrada a la tabla
    table->push_back(entry);

    // Ordena la tabla
    sort();

    // Deja solo las 10 primeras entradas
    if ((int)table->size() > 10)
    {
        table->resize(10);
    }
}

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

    std::sort(table->begin(), table->end(), customLess);
}

// Carga la tabla con los datos de un fichero
bool ManageHiScoreTable::loadFromFile(std::string filePath)
{
    clear();

    bool success = true;
    const std::string filename = filePath.substr(filePath.find_last_of("\\/") + 1);
    SDL_RWops *file = SDL_RWFromFile(filePath.c_str(), "r+b");

    if (file)
    {
#ifdef DEBUG
        std::cout << "Reading file: " << filename.c_str() << std::endl;
#endif
        for (int i = 0; i < (int)table->size(); ++i)
        {
            int nameSize = 0;

            if (SDL_RWread(file, &table->at(i).score, sizeof(int), 1) == 0)
            {
                success = false;
                break;
            }

            if (SDL_RWread(file, &nameSize, sizeof(int), 1) == 0)
            {
                success = false;
                break;
            }

            char *name = (char *)malloc(nameSize + 1);
            if (SDL_RWread(file, name, sizeof(char) * nameSize, 1) == 0)
            {
                success = false;
                free(name);
                break;
            }
            else
            {
                name[nameSize] = 0;
                table->at(i).name = name;
                free(name);
            }
        }

        SDL_RWclose(file);
    }

    if (!success)
    {
        clear();
    }

    return success;
}

// Guarda la tabla en un fichero
bool ManageHiScoreTable::saveToFile(std::string filePath)
{
    bool success = true;
    const std::string fileName = filePath.substr(filePath.find_last_of("\\/") + 1);
    SDL_RWops *file = SDL_RWFromFile(filePath.c_str(), "w+b");

    if (file)
    {
        // Guarda los datos
        for (int i = 0; i < (int)table->size(); ++i)
        {
            SDL_RWwrite(file, &table->at(i).score, sizeof(int), 1);
            const int nameSize = (int)table->at(i).name.size();
            SDL_RWwrite(file, &nameSize, sizeof(int), 1);
            SDL_RWwrite(file, table->at(i).name.c_str(), nameSize, 1);
        }

#ifdef DEBUG
        std::cout << "Writing file: " << fileName.c_str() << std::endl;
#endif
        // Cierra el fichero
        SDL_RWclose(file);
    }
    else
    {
#ifdef DEBUG
        std::cout << "Error: Unable to save " << fileName.c_str() << " file! " << SDL_GetError() << std::endl;
#endif
    }
    return success;
}