#include "options.h"
#include <SDL2/SDL_video.h>  // for SDL_WINDOW_FULLSCREEN_DESKTOP
#include <algorithm>         // for find_if
#include <cctype>            // for isspace
#include <fstream>           // for basic_ostream, operator<<, basic_ofstream
#include <functional>        // for function
#include <iostream>          // for cout, cerr
#include <sstream>           // for basic_istringstream
#include <string>            // for char_traits, string, operator<<, hash
#include <unordered_map>     // for unordered_map, operator==, _Node_const_i...
#include <utility>           // for pair

// Variables
Options options;

bool setOptions(const std::string &var, const std::string &value);

// Crea e inicializa las opciones del programa
void initOptions()
{
    options = Options();

#ifdef DEBUG
    options.section = SectionState(Section::LOGO, Subsection::LOGO_TO_INTRO);
    options.console = true;
#else
    options.section = SectionState(Section::LOGO, Subsection::LOGO_TO_INTRO);
    options.console = false;
#endif
}

// Carga las opciones desde un fichero
bool loadOptionsFromFile(const std::string &file_path)
{
    // Indicador de éxito en la carga
    bool success = true;

    // Versión actual del fichero
    const std::string configVersion = options.version;
    options.version = "";

    // Variables para manejar el fichero
    std::ifstream file(file_path);
    
    // Si el fichero se puede abrir
    if (file.good())
    {
        // Procesa el fichero línea a línea
        if (options.console)
        {
            std::cout << "Reading file config.txt\n";
        }
        std::string line;
        while (std::getline(file, line))
        {
            // Elimina espacios en blanco iniciales y finales
            line = std::string(std::find_if(line.begin(), line.end(), [](int ch)
                                            { return !std::isspace(ch); }),
                               line.end());
            line.erase(std::find_if(line.rbegin(), line.rend(), [](int ch)
                                    { return !std::isspace(ch); })
                           .base(),
                       line.end());

            // Ignora líneas vacías o comentarios
            if (line.empty() || line[0] == '#')
            {
                continue;
            }

            // Usa un stringstream para dividir la línea en dos partes
            std::istringstream iss(line);
            std::string key, value;

            if (iss >> key >> value)
            {
                if (!setOptions(key, value))
                {
                    if (options.console)
                    {
                        std::cout << "Warning: file config.txt\n";
                        std::cout << "unknown parameter " << key << std::endl;
                    }
                    success = false;
                }
            }
        }

        // Cierra el fichero
        if (options.console)
        {
            std::cout << "Closing file config.txt\n\n";
        }
        file.close();
    }
    else
    {
        // Crea el fichero con los valores por defecto
        saveOptionsToFile(file_path);
    }

    // Si la versión de fichero no coincide, crea un fichero nuevo con los valores por defecto
    if (configVersion != options.version)
    {
        initOptions();
        saveOptionsToFile(file_path);
        if (options.console)
        {
            std::cout << "Wrong config file: initializing options.\n\n";
        }
    }

    return success;
}

// Guarda las opciones en un fichero
bool saveOptionsToFile(const std::string &file_path)
{
    // Crea y abre el fichero de texto
    std::ofstream file(file_path);
    bool success = file.is_open(); // Verifica si el archivo se abrió correctamente

    if (!success) // Si no se pudo abrir el archivo, muestra un mensaje de error y devuelve false
    {
        if (options.console)
        {
            std::cerr << "Error: Unable to open file " << file_path << " for writing." << std::endl;
        }
        return false;
    }

    if (options.console)
    {
        std::cout << file_path << " open for writing" << std::endl;
    }

    // Escribe en el fichero
    file << "# Versión de la configuración\n";
    file << "version " << options.version << "\n";

    file << "\n## CONTROL\n";
    file << "# Esquema de control: 0 = Cursores, 1 = OPQ, 2 = WAD\n";
    file << "keys " << static_cast<int>(options.keys) << "\n";

    file << "\n## WINDOW\n";
    file << "# Zoom de la ventana: 1 = Normal, 2 = Doble, 3 = Triple, ...\n";
    file << "window.zoom " << options.window.zoom << "\n";

    file << "\n## VIDEO\n";
    file << "# Modo de video: 0 = Ventana, 1 = Pantalla completa, 2 = Pantalla completa (escritorio)\n";
    file << "video.mode " << options.video.mode << "\n\n";
    file << "# Filtro de pantalla: 0 = Nearest, 1 = Linear\n";
    file << "video.filter " << static_cast<int>(options.video.filter) << "\n\n";
    file << "# Shaders: 1 = Activado, 0 = Desactivado\n";
    file << "video.shaders " << boolToString(options.video.shaders) << "\n\n";
    file << "# Sincronización vertical: 1 = Activado, 0 = Desactivado\n";
    file << "video.vertical_sync " << boolToString(options.video.vertical_sync) << "\n\n";
    file << "# Escalado entero: 1 = Activado, 0 = Desactivado\n";
    file << "video.integer_scale " << boolToString(options.video.integer_scale) << "\n\n";
    file << "# Mantener aspecto: 1 = Activado, 0 = Desactivado\n";
    file << "video.keep_aspect " << boolToString(options.video.keep_aspect) << "\n\n";
    file << "# Borde: 1 = Activado, 0 = Desactivado\n";
    file << "video.border.enabled " << boolToString(options.video.border.enabled) << "\n\n";
    file << "# Ancho del borde\n";
    file << "video.border.width " << options.video.border.width << "\n\n";
    file << "# Alto del borde\n";
    file << "video.border.height " << options.video.border.height << "\n\n";
    file << "# Paleta:\n";
    file << "video.palette " << options.video.palette << "\n";

    // Cierra el fichero
    file.close();

    return success;
}

bool setOptions(const std::string &var, const std::string &value)
{
    static const std::unordered_map<std::string, std::function<void(const std::string &)>> optionHandlers = {
        {"version", [](const std::string &v)
         { options.version = v; }},
        {"keys", [](const std::string &v)
         {
             int val = safeStoi(v, static_cast<int>(DEFAULT_CONTROL_SCHEME));
             if (val == static_cast<int>(ControlScheme::CURSOR) || val == static_cast<int>(ControlScheme::OPQA) || val == static_cast<int>(ControlScheme::WASD))
             {
                 options.keys = static_cast<ControlScheme>(val);
             }
             else
             {
                 options.keys = DEFAULT_CONTROL_SCHEME; 
             }
         }},
        {"window.zoom", [](const std::string &v)
         {
             int val = safeStoi(v, DEFAULT_WINDOW_ZOOM);
             if (val > 0)
             {
                 options.window.zoom = val;
             }
             else
             {
                 options.window.zoom = DEFAULT_WINDOW_ZOOM; 
             }
         }},
        {"video.mode", [](const std::string &v)
         {
             int val = safeStoi(v, 0);
             if (val == 0 || val == SDL_WINDOW_FULLSCREEN_DESKTOP)
             {
                 options.video.mode = val;
             }
             else
             {
                 options.video.mode = 0; 
             }
         }},
        {"video.filter", [](const std::string &v)
         {
             int val = safeStoi(v, static_cast<int>(DEFAULT_VIDEO_FILTER));
             if (val == static_cast<int>(ScreenFilter::NEAREST) || val == static_cast<int>(ScreenFilter::LINEAR))
             {
                 options.video.filter = static_cast<ScreenFilter>(val);
             }
             else
             {
                 options.video.filter = DEFAULT_VIDEO_FILTER; 
             }
         }},
        {"video.shaders", [](const std::string &v)
         { options.video.shaders = stringToBool(v); }},
        {"video.vertical_sync", [](const std::string &v)
         { options.video.vertical_sync = stringToBool(v); }},
        {"video.integer_scale", [](const std::string &v)
         { options.video.integer_scale = stringToBool(v); }},
        {"video.keep_aspect", [](const std::string &v)
         { options.video.keep_aspect = stringToBool(v); }},
        {"video.border.enabled", [](const std::string &v)
         { options.video.border.enabled = stringToBool(v); }},
        {"video.border.width", [](const std::string &v)
         {
             int val = safeStoi(v, DEFAULT_BORDER_WIDTH);
             if (val > 0)
             {
                 options.video.border.width = val;
             }
             else
             {
                 options.video.border.width = DEFAULT_BORDER_WIDTH; 
             }
         }},
        {"video.border.height", [](const std::string &v)
         {
             int val = safeStoi(v, DEFAULT_BORDER_HEIGHT);
             if (val > 0)
             {
                 options.video.border.height = val;
             }
             else
             {
                 options.video.border.height = DEFAULT_BORDER_HEIGHT; 
             }
         }},
        {"video.palette", [](const std::string &v)
         {
             std::string val = v;
             if (val == "arreglar" || val == "esto")
             {
                 options.video.palette = val;
             }
             else
             {
                 options.video.palette = DEFAULT_PALETTE;
             }
         }}};

    auto it = optionHandlers.find(var);
    if (it != optionHandlers.end())
    {
        it->second(value);
        return true;
    }
    return false;
}
