#include "cheevos.h"
#include <SDL2/SDL_error.h> // Para SDL_GetError
#include <SDL2/SDL_rwops.h> // Para SDL_RWFromFile, SDL_RWclose, SDL_RWwrite
#include <stddef.h>         // Para NULL
#include <iostream>         // Para basic_ostream, operator<<, cout, endl
#include "notifier.h"       // Para Screen
#include "utils.h"          // Para options_t
#include "options.h"
#include <fstream> // Para fstream

// [SINGLETON]
Cheevos *Cheevos::cheevos_ = nullptr;

// [SINGLETON] Crearemos el objeto con esta función estática
void Cheevos::init(const std::string &file)
{
    Cheevos::cheevos_ = new Cheevos(file);
}

// [SINGLETON] Destruiremos el objeto con esta función estática
void Cheevos::destroy()
{
    delete Cheevos::cheevos_;
}

// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
Cheevos *Cheevos::get()
{
    return Cheevos::cheevos_;
}

// Constructor
Cheevos::Cheevos(const std::string &file)
    : file_(file)
{
    init();
    loadFromFile();
}

// Destructor
Cheevos::~Cheevos()
{
    saveToFile();
}

// Inicializa los logros
void Cheevos::init()
{
    cheevos_list_.clear();
    cheevos_list_.emplace_back(1, "SHINY THINGS", "Get 25% of the items", 2);
    cheevos_list_.emplace_back(2, "HALF THE WORK", "Get 50% of the items", 2);
    cheevos_list_.emplace_back(3, "GETTING THERE", "Get 75% of the items", 2);
    cheevos_list_.emplace_back(4, "THE COLLECTOR", "Get 100% of the items", 2);
    cheevos_list_.emplace_back(5, "WANDERING AROUND", "Visit 20 rooms", 2);
    cheevos_list_.emplace_back(6, "I GOT LOST", "Visit 40 rooms", 2);
    cheevos_list_.emplace_back(7, "I LIKE TO EXPLORE", "Visit all rooms", 2);
    cheevos_list_.emplace_back(8, "FINISH THE GAME", "Complete the game", 2);
    cheevos_list_.emplace_back(9, "I WAS SUCKED BY A HOLE", "Complete the game without entering the jail", 2);
    cheevos_list_.emplace_back(10, "MY LITTLE PROJECTS", "Complete the game with all items", 2);
    cheevos_list_.emplace_back(11, "I LIKE MY MULTICOLOURED FRIENDS", "Complete the game without dying", 2);
    cheevos_list_.emplace_back(12, "SHIT PROJECTS DONE FAST", "Complete the game in under 30 minutes", 2);
}

// Busca un logro por id y devuelve el indice
int Cheevos::find(int id)
{
    for (int i = 0; i < (int)cheevos_list_.size(); ++i)
    {
        if (cheevos_list_[i].id == id)
        {
            return i;
        }
    }

    return -1;
}

// Desbloquea un logro
void Cheevos::unlock(int id)
{
    const int index = find(id);

    // Si el índice es inválido, el logro no es válido, ya está completado o el sistema de logros no está habilitado, no hacemos nada
    if (index == -1 || !cheevos_list_.at(index).valid || cheevos_list_.at(index).completed || !enabled_)
    {
        return;
    }

    // Marcar el logro como completado
    cheevos_list_.at(index).completed = true;
    // Mostrar notificación en la pantalla
    Notifier::get()->show("ACHIEVEMENT UNLOCKED!", cheevos_list_.at(index).caption, cheevos_list_.at(index).icon);
    // Guardar el estado de los logros
    saveToFile();
}

// Invalida un logro
void Cheevos::invalidate(int id)
{
    const int index = find(id);

    // Si el índice es válido, se invalida el logro
    if (index != -1)
    {
        cheevos_list_.at(index).valid = false;
    }
}

// Carga el estado de los logros desde un fichero
void Cheevos::loadFromFile()
{
    std::ifstream file(this->file_, std::ios::binary);

    // El fichero no existe
    if (!file)
    {
        if (options.console)
        {
            std::cout << "Warning: Unable to open file! Creating new file..." << std::endl;
        }

        // Crea el fichero en modo escritura (binario)
        std::ofstream newFile(this->file_, std::ios::binary);

        if (newFile)
        {
            if (options.console)
            {
                std::cout << "New file created!" << std::endl;
            }

            // Guarda la información
            for (const auto &cheevo : cheevos_list_)
            {
                newFile.write(reinterpret_cast<const char *>(&cheevo.completed), sizeof(bool));
            }
        }
        else
        {
            if (options.console)
            {
                std::cerr << "Error: Unable to create file!" << std::endl;
            }
        }
    }
    // El fichero existe
    else
    {
        if (options.console)
        {
            std::cout << "Reading file...!" << std::endl;
        }

        // Carga los datos
        for (auto &cheevo : cheevos_list_)
        {
            file.read(reinterpret_cast<char *>(&cheevo.completed), sizeof(bool));
        }
    }
}

// Guarda el estado de los logros en un fichero
void Cheevos::saveToFile()
{
    // Abre el fichero en modo escritura (binario)
    SDL_RWops *file = SDL_RWFromFile(this->file_.c_str(), "w+b");
    if (file != NULL)
    {
        // Guarda la información
        for (int i = 0; i < (int)cheevos_list_.size(); ++i)
        {
            SDL_RWwrite(file, &cheevos_list_[i].completed, sizeof(bool), 1);
        }

        // Cierra el fichero
        SDL_RWclose(file);
    }
    else
    {
        if (options.console)
        {
            std::cout << "Error: Unable to save file! " << SDL_GetError() << std::endl;
        }
    }
}

// Devuelve el número total de logros desbloqueados
int Cheevos::unlocked()
{
    int count = 0;
    for (auto cheevo : cheevos_list_)
    {
        if (cheevo.completed)
        {
            count++;
        }
    }
    return count;
}