#pragma once

#include <SDL2/SDL_rect.h>   // Para SDL_Rect, SDL_Point
#include <SDL2/SDL_stdinc.h> // Para Uint8, Uint32
#include <string>            // Para string, basic_string
#include "utils.h"
#include "screen.h"

// Secciones del programa
enum class Section
{
    LOGO,
    LOADING_SCREEN,
    TITLE,
    CREDITS,
    GAME,
    DEMO,
    GAME_OVER,
    ENDING,
    ENDING2,
    QUIT
};

// Subsecciones
enum class Subsection
{
    NONE,
    LOGO_TO_INTRO,
    LOGO_TO_TITLE,
    TITLE_WITH_LOADING_SCREEN,
    TITLE_WITHOUT_LOADING_SCREEN
};

// Posiciones de las notificaciones
enum class NotificationPosition
{
    UPPER_LEFT,
    UPPER_CENTER,
    UPPER_RIGHT,
    MIDDLE_LEFT,
    MIDDLE_RIGHT,
    BOTTOM_LEFT,
    BOTTOM_CENTER,
    BOTTOM_RIGHT
};

// Tipos de control de teclado
enum class ControlScheme
{
    CURSOR,
    OPQA,
    WASD
};

// Constantes
constexpr int DEFAULT_GAME_WIDTH = 256;                                                          // Ancho de la ventana por defecto
constexpr int DEFAULT_GAME_HEIGHT = 192;                                                         // Alto de la ventana por defecto
constexpr int DEFAULT_WINDOW_ZOOM = 2;                                                           // Zoom de la ventana por defecto
constexpr int DEFAULT_VIDEO_MODE = 0;                                                            // Modo de pantalla completa por defecto
constexpr ScreenFilter DEFAULT_VIDEO_FILTER = ScreenFilter::NEAREST;                             // Filtro por defecto
constexpr bool DEFAULT_VIDEO_VERTICAL_SYNC = true;                                               // Vsync activado por defecto
constexpr bool DEFAULT_VIDEO_SHADERS = false;                                                    // Shaders desactivados por defecto
constexpr bool DEFAULT_VIDEO_INTEGER_SCALE = true;                                               // Escalado entero activado por defecto
constexpr bool DEFAULT_VIDEO_KEEP_ASPECT = true;                                                 // Mantener aspecto activado por defecto
constexpr bool DEFAULT_BORDER_ENABLED = true;                                                    // Borde activado por defecto
constexpr int DEFAULT_BORDER_WIDTH = 32;                                                         // Ancho del borde por defecto
constexpr int DEFAULT_BORDER_HEIGHT = 24;                                                        // Alto del borde por defecto
constexpr Palette DEFAULT_PALETTE = Palette::ZXSPECTRUM;                                         // Paleta por defecto
constexpr Section DEFAULT_SECTION = Section::LOGO;                                               // Sección por defecto
constexpr Subsection DEFAULT_SUBSECTION = Subsection::LOGO_TO_INTRO;                             // Subsección por defecto
constexpr ControlScheme DEFAULT_CONTROL_SCHEME = ControlScheme::CURSOR;                          // Control por defecto
constexpr NotificationPosition DEFAULT_NOTIFICATION_POSITION = NotificationPosition::UPPER_LEFT; // Posición de las notificaciones por defecto
constexpr bool DEFAULT_NOTIFICATION_SOUND = true;                                                // Sonido de las notificaciones por defecto
const Color DEFAULT_NOTIFICATION_COLOR = Color(48, 48, 48);                                      // Color de las notificaciones por defecto
constexpr bool DEFAULT_CONSOLE = false;                                                          // Consola desactivada por defecto
constexpr std::string DEFAULT_VERSION = "v1.09";                                                 // Versión por defecto

// Estructura para las opciones de las notificaciones
struct OptionsNotification
{
    NotificationPosition pos; // Ubicación de las notificaciones en pantalla
    bool sound;               // Indica si las notificaciones suenan
    Color color;              // Color de las notificaciones

    // Constructor por defecto
    OptionsNotification()
        : pos(DEFAULT_NOTIFICATION_POSITION),
          sound(DEFAULT_NOTIFICATION_SOUND),
          color(DEFAULT_NOTIFICATION_COLOR) {}

    // Constructor
    OptionsNotification(NotificationPosition p, bool s, Color c)
        : pos(p),
          sound(s),
          color(c) {}

    // Método que devuelve la posición horizontal
    std::string getHorizontalPosition() const
    {
        switch (pos)
        {
        case NotificationPosition::UPPER_LEFT:
        case NotificationPosition::MIDDLE_LEFT:
        case NotificationPosition::BOTTOM_LEFT:
            return "LEFT";
        case NotificationPosition::UPPER_CENTER:
        case NotificationPosition::BOTTOM_CENTER:
            return "CENTER";
        case NotificationPosition::UPPER_RIGHT:
        case NotificationPosition::MIDDLE_RIGHT:
        case NotificationPosition::BOTTOM_RIGHT:
            return "RIGHT";
        }
        return "UNKNOWN";
    }

    // Método que devuelve la posición vertical
    std::string getVerticalPosition() const
    {
        switch (pos)
        {
        case NotificationPosition::UPPER_LEFT:
        case NotificationPosition::UPPER_CENTER:
        case NotificationPosition::UPPER_RIGHT:
            return "UPPER";
        case NotificationPosition::MIDDLE_LEFT:
        case NotificationPosition::MIDDLE_RIGHT:
            return "MIDDLE";
        case NotificationPosition::BOTTOM_LEFT:
        case NotificationPosition::BOTTOM_CENTER:
        case NotificationPosition::BOTTOM_RIGHT:
            return "BOTTOM";
        }
        return "UNKNOWN";
    }
};

// Estructura para saber la seccion y subseccion del programa
struct SectionState
{
    Section section;
    Subsection subsection;

    // Constructor por defecto
    SectionState()
        : section(DEFAULT_SECTION),
          subsection(DEFAULT_SUBSECTION) {}

    // Constructor
    SectionState(Section s, Subsection ss)
        : section(s),
          subsection(ss) {}
};

// Estructura para albergar trucos
struct Cheat
{
    enum class CheatState : bool
    {
        DISABLED = false,
        ENABLED = true
    };

    CheatState infinite_lives; // Indica si el jugador dispone de vidas infinitas
    CheatState invincible;     // Indica si el jugador puede morir
    CheatState jail_is_open;   // Indica si la Jail está abierta
    CheatState alternate_skin; // Indica si se usa una skin diferente para el jugador

    // Constructor por defecto
    Cheat()
        : infinite_lives(CheatState::DISABLED),
          invincible(CheatState::DISABLED),
          jail_is_open(CheatState::DISABLED),
          alternate_skin(CheatState::DISABLED) {}

    // Constructor
    Cheat(CheatState il, CheatState i, CheatState je, CheatState as)
        : infinite_lives(il),
          invincible(i),
          jail_is_open(je),
          alternate_skin(as) {}

    // Método para comprobar si alguno de los tres primeros trucos está activo
    bool enabled() const
    {
        return infinite_lives == CheatState::ENABLED ||
               invincible == CheatState::ENABLED ||
               jail_is_open == CheatState::ENABLED;
    }
};

// Estructura para almacenar estadísticas
struct OptionsStats
{
    int rooms;                   // Cantidad de habitaciones visitadas
    int items;                   // Cantidad de items obtenidos
    std::string worst_nightmare; // Habitación con más muertes acumuladas

    // Constructor por defecto
    OptionsStats()
        : rooms(0),
          items(0),
          worst_nightmare("") {}

    // Constructor
    OptionsStats(int r, int i, std::string wn)
        : rooms(r),
          items(i),
          worst_nightmare(wn) {}
};

// Estructura con opciones de la ventana
struct OptionsWindow
{
    int zoom; // Zoom de la ventana

    // Constructor por defecto
    OptionsWindow()
        : zoom(DEFAULT_WINDOW_ZOOM) {}

    // Constructor
    OptionsWindow(int z)
        : zoom(z) {}
};

// Estructura para gestionar el borde de la pantalla
struct Border
{
    bool enabled; // Indica si se ha de mostrar el borde
    int width;    // Ancho del borde
    int height;   // Alto del borde

    // Constructor por defecto
    Border()
        : enabled(DEFAULT_BORDER_ENABLED),
          width(DEFAULT_BORDER_WIDTH),
          height(DEFAULT_BORDER_HEIGHT) {}

    // Constructor
    Border(bool e, int w, int h)
        : enabled(e),
          width(w),
          height(h) {}
};

// Estructura para las opciones de video
struct OptionsVideo
{
    Uint32 mode;         // Contiene el valor del modo de pantalla completa
    ScreenFilter filter; // Filtro usado para el escalado de la imagen
    bool vertical_sync;  // Indica si se quiere usar vsync o no
    bool shaders;        // Indica si se van a usar shaders o no
    bool integer_scale;  // Indica si el escalado de la imagen ha de ser entero en el modo a pantalla completa
    bool keep_aspect;    // Indica si se ha de mantener la relación de aspecto al poner el modo a pantalla completa
    Border border;       // Borde de la pantalla
    Palette palette;     // Paleta de colores a usar en el juego

    // Constructor por defecto
    OptionsVideo()
        : mode(DEFAULT_VIDEO_MODE),
          filter(DEFAULT_VIDEO_FILTER),
          vertical_sync(DEFAULT_VIDEO_VERTICAL_SYNC),
          shaders(DEFAULT_VIDEO_SHADERS),
          integer_scale(DEFAULT_VIDEO_INTEGER_SCALE),
          keep_aspect(DEFAULT_VIDEO_KEEP_ASPECT),
          border(Border()),
          palette(DEFAULT_PALETTE) {}

    // Constructor
    OptionsVideo(Uint32 m, ScreenFilter f, bool vs, bool s, bool is, bool ka, Border b, Palette p)
        : mode(m),
          filter(f),
          vertical_sync(vs),
          shaders(s),
          integer_scale(is),
          keep_aspect(ka),
          border(b),
          palette(p) {}
};

// Estructura para las opciones de juego
struct OptionsGame
{
    int width;  // Ancho de la resolucion del juego
    int height; // Alto de la resolucion del juego

    // Constructor por defecto
    OptionsGame()
        : width(DEFAULT_GAME_WIDTH),
          height(DEFAULT_GAME_HEIGHT) {}

    // Constructor
    OptionsGame(int w, int h)
        : width(w),
          height(h) {}
};

// Estructura con todas las opciones de configuración del programa
struct Options
{
    std::string version;               // Versión del fichero de configuración. Sirve para saber si las opciones son compatibles
    bool console;                      // Indica si ha de mostrar información por la consola de texto
    Cheat cheats;                      // Contiene trucos y ventajas para el juego
    OptionsGame game;                  // Opciones de juego
    OptionsVideo video;                // Opciones de video
    OptionsStats stats;                // Datos con las estadisticas de juego
    OptionsNotification notifications; // Opciones relativas a las notificaciones;
    OptionsWindow window;              // Opciones relativas a la ventana
    ControlScheme keys;                // Teclas usadas para jugar
    SectionState section;              // Sección actual del programa

    // Constructor por defecto
    Options()
        : version(DEFAULT_VERSION),
          console(DEFAULT_CONSOLE),
          cheats(Cheat()),
          game(OptionsGame()),
          video(OptionsVideo()),
          stats(OptionsStats()),
          notifications(OptionsNotification()),
          window(OptionsWindow()),
          keys(DEFAULT_CONTROL_SCHEME),
          section(SectionState()) {}

    // Constructor
    Options(std::string cv, bool c, Cheat ch, OptionsGame g, OptionsVideo v, OptionsStats s, OptionsNotification n, OptionsWindow sw, ControlScheme k, SectionState sec)
        : version(cv),
          console(c),
          cheats(ch),
          game(g),
          video(v),
          stats(s),
          notifications(n),
          window(sw),
          keys(k),
          section(sec) {}
};

extern Options options;

// Crea e inicializa las opciones del programa
void initOptions();

// Carga las opciones desde un fichero
bool loadOptionsFromFile(const std::string &file_path);

// Guarda las opciones a un fichero
bool saveOptionsToFile(const std::string &file_path);