#include "stats.h"
#include <fstream>   // for basic_ostream, basic_ifstream, basic_istream
#include <sstream>   // for basic_stringstream
#include "options.h" // for Options, OptionsStats, options

// Constructor
Stats::Stats(const std::string &file, const std::string &buffer)
    : bufferPath(buffer),
      filePath(file) {}

// Destructor
Stats::~Stats()
{
    // Vuelca los datos del buffer en la lista de estadisticas
    updateListFromBuffer();

    // Calcula cual es la habitación con más muertes
    checkWorstNightmare();

    // Guarda las estadísticas
    saveToFile(bufferPath, bufferList);
    saveToFile(filePath, list);

    bufferList.clear();
    list.clear();
    dictionary.clear();
}

// Inicializador
void Stats::init()
// Se debe llamar a este procedimiento una vez se haya creado el diccionario numero-nombre
{
    loadFromFile(bufferPath, bufferList);
    loadFromFile(filePath, list);

    // Vuelca los datos del buffer en la lista de estadisticas
    updateListFromBuffer();
}

// Añade una muerte a las estadisticas
void Stats::addDeath(const std::string &name)
{
    // Primero busca si ya hay una entrada con ese nombre
    const int index = findByName(name, bufferList);
    if (index != -1)
    {
        bufferList[index].died++;
    }

    // En caso contrario crea la entrada
    else
    {
        StatsData item;
        item.name = name;
        item.visited = 0;
        item.died = 1;
        bufferList.push_back(item);
    }
}

// Añade una visita a las estadisticas
void Stats::addVisit(const std::string &name)
{
    // Primero busca si ya hay una entrada con ese nombre
    const int index = findByName(name, bufferList);
    if (index != -1)
    {
        bufferList[index].visited++;
    }

    // En caso contrario crea la entrada
    else
    {
        StatsData item;
        item.name = name;
        item.visited = 1;
        item.died = 0;
        bufferList.push_back(item);
    }
}

// Busca una entrada en la lista por nombre
int Stats::findByName(const std::string &name, const std::vector<StatsData> &list)
{
    int i = 0;

    for (const auto &l : list)
    {
        if (l.name == name)
        {
            return i;
        }
        i++;
    }

    return -1;
}

// Carga las estadisticas desde un fichero
bool Stats::loadFromFile(const std::string &file_path, std::vector<StatsData> &list)
{
    list.clear();

    // Indicador de éxito en la carga
    bool success = true;

    // Variables para manejar el fichero
    std::ifstream file(file_path);

    // Si el fichero se puede abrir
    if (file.good())
    {
        std::string line;
        // Procesa el fichero linea a linea
        while (std::getline(file, line))
        {
            // Comprueba que la linea no sea un comentario
            if (line.substr(0, 1) != "#")
            {
                StatsData stat;
                std::stringstream ss(line);
                std::string tmp;

                // Obtiene el nombre
                getline(ss, tmp, ';');
                stat.name = tmp;

                // Obtiene las visitas
                getline(ss, tmp, ';');
                stat.visited = std::stoi(tmp);

                // Obtiene las muertes
                getline(ss, tmp, ';');
                stat.died = std::stoi(tmp);

                list.push_back(stat);
            }
        }

        // Cierra el fichero
        file.close();
    }

    // El fichero no existe
    else
    {
        // Crea el fichero con los valores por defecto
        saveToFile(file_path, list);
    }

    return success;
}

// Guarda las estadisticas en un fichero
void Stats::saveToFile(const std::string &file_path, const std::vector<StatsData> &list)
{
    // Crea y abre el fichero de texto
    std::ofstream file(file_path);

    // Escribe en el fichero
    file << "# ROOM NAME;VISITS;DEATHS" << std::endl;
    for (const auto &item : list)
    {
        file << item.name << ";" << item.visited << ";" << item.died << std::endl;
    }

    // Cierra el fichero
    file.close();
}

// Calcula cual es la habitación con más muertes
void Stats::checkWorstNightmare()
{
    int deaths = 0;
    for (const auto &item : list)
    {
        if (item.died > deaths)
        {
            deaths = item.died;
            options.stats.worst_nightmare = item.name;
        }
    }
}

// Añade una entrada al diccionario
void Stats::addDictionary(const std::string &number, const std::string &name)
{
    dictionary.push_back({number, name});
}

// Vuelca los datos del buffer en la lista de estadisticas
void Stats::updateListFromBuffer()
{
    // Actualiza list desde bufferList
    for (const auto &buffer : bufferList)
    {
        int index = findByName(buffer.name, list);

        if (index != -1)
        { // Encontrado. Aumenta sus estadisticas
            list[index].visited += buffer.visited;
            list[index].died += buffer.died;
        }
        else
        { // En caso contrario crea la entrada
            StatsData item;
            item.name = buffer.name;
            item.visited = buffer.visited;
            item.died = buffer.died;
            list.push_back(item);
        }
    }

    saveToFile(bufferPath, bufferList);
    saveToFile(filePath, list);
}