#pragma once

#include <SDL3/SDL.h>

#include <cstdint>
#include <string>  // for string, basic_string
#include <vector>  // for vector

#include "utils/utils.h"  // for Color
class Text;
namespace Ja {
    struct Sound;
}  // namespace Ja

// Tipos de fondos para el menú
constexpr int MENU_BACKGROUND_TRANSPARENT = 0;
constexpr int MENU_BACKGROUND_SOLID = 1;

// Tipos de archivos de audio
constexpr int SOUND_ACCEPT = 0;
constexpr int SOUND_MOVE = 1;
constexpr int SOUND_CANCEL = 2;

// Opciones de menú
constexpr int MENU_NO_OPTION = -1;

// Clase Menu
class Menu {
   public:
    explicit Menu(SDL_Renderer *renderer, const std::string &file = "");  // Constructor
    ~Menu();                                                              // Destructor

    auto loadFromBytes(const std::vector<uint8_t> &bytes, const std::string &name_for_logs = "") -> bool;  // Carga el menu desde bytes en memoria
    void loadAudioFile(const std::string &file, int sound);                                              // Carga los ficheros de audio

    [[nodiscard]] auto getName() const -> const std::string &;  // Obtiene el nombre del menu
    auto getItemSelected() -> int;                              // Obtiene el valor de la variable

    void reset();        // Deja el menu apuntando al primer elemento
    void checkInput();   // Gestiona la entrada de teclado y mando durante el menu
    void update();       // Actualiza la logica del menu
    void render();       // Pinta el menu en pantalla

    void setBackgroundColor(Color color, int alpha);    // Establece el color del rectangulo de fondo
    void setSelectorColor(Color color, int alpha);      // Establece el color del rectangulo del selector
    void setSelectorTextColor(Color color);             // Establece el color del texto del selector

    void centerMenuOnX(int value = 0);    // Centra el menu respecto a un punto en el eje X
    void centerMenuOnY(int value);        // Centra el menu respecto a un punto en el eje Y
    void centerMenuElementsOnX();         // Centra los elementos del menu en el eje X

    struct Item {
        std::string label;  // Texto
        SDL_Rect rect;      // Rectangulo que delimita el elemento
        int h_padding_down;   // Espaciado bajo el elemento
        bool selectable;    // Indica si se puede seleccionar
        bool greyed;        // Indica si ha de aparecer con otro color mas oscuro
        bool linked_down;    // Indica si el elemento actual y el siguiente se tratan como uno solo. Afecta al selector
        bool linked_up;      // Indica si el elemento actual y el anterior se tratan como uno solo. Afecta al selector
        bool visible;       // Indica si el elemento es visible
        bool line;          // Indica si el elemento lleva una linea a continuación
    };

    void addItem(Item new_item);                                  // Añade un item al menu
    void setItemCaption(int index, const std::string &text);      // Cambia el texto de un item
    void setDefaultActionWhenCancel(int item);                    // Establece el indice del item que se usará por defecto al cancelar el menu
    void setSelectorPos(int index);                               // Coloca el selector en una posición específica
    void setSelectable(int index, bool value);                    // Establece el estado seleccionable de un item
    void setGreyed(int index, bool value);                        // Establece el estado agrisado de un item
    void setLinkedDown(int index, bool value);                    // Establece el estado de enlace de un item
    void setVisible(int index, bool value);                       // Establece el estado de visibilidad de un item

    void setName(const std::string &name);                              // Establece el nombre del menu
    void setPos(int x, int y);                                          // Establece la posición del menu
    void setBackgroundType(int value);                                  // Establece el tipo de fondo del menu
    void setText(const std::string &font_png, const std::string &font_txt);  // Establece la fuente de texto que se utilizará
    void setRectSize(int w = 0, int h = 0);                             // Establece el rectangulo de fondo del menu

   private:
    struct Rectangle {
        SDL_Rect rect;  // Rectangulo
        Color color;    // Color
        int a;          // Transparencia
    };

    struct Selector {
        float origin_y;            // Coordenada de origen
        float target_y;            // Coordenada de destino
        float desp_y;              // Cantidad de pixeles que se desplaza el selector en cada salto: (target - origin) / numJumps
        bool moving;              // Indica si el selector está avanzando hacia el destino
        float origin_h;            // Altura de origen
        float target_h;            // Altura de destino
        float inc_h;               // Cantidad de pixels que debe incrementar o decrementar el selector en cada salto
        bool resizing;            // Indica si el selector está cambiando de tamaño
        float y;                  // Coordenada actual, usado para el desplazamiento
        float h;                  // Altura actual, usado para el cambio de tamaño
        int num_jumps;             // Numero de pasos preestablecido para llegar al destino
        int index;                // Elemento del menu que tiene el foco
        int previous_index;        // Elemento que tenia el foco previamente
        Color previous_item_color;  // Color del item nque tenia el foco previamente
        SDL_Rect rect;            // Rectangulo del selector
        Color color;              // Color del selector
        Color item_color;          // Color del item
        Color jump_item_colors[8];  // Transición de colores para el item seleccionado
        int item_color_index;       // Indice del color de transición para el item seleccionado
        int a;                    // Cantidad de transparencia para el rectangulo del selector
    };

    auto load(const std::string &file_path) -> bool;                                       // Carga la configuración del menu desde un archivo de texto
    auto parseFromStream(std::istream &file, const std::string &filename) -> bool;         // Parser compartido (recibe cualquier istream)
    auto setVars(const std::string &var, const std::string &value) -> bool;                // Asigna variables a partir de dos cadenas
    static auto setItem(Item *item, const std::string &var, const std::string &value) -> bool;  // Asigna variables a partir de dos cadenas

    void reorganize();              // Actualiza el menu para recolocarlo correctamente y establecer el tamaño
    void increaseSelectorIndex();   // Deja el menu apuntando al siguiente elemento
    void decreaseSelectorIndex();   // Deja el menu apuntando al elemento anterior
    void updateSelector();          // Actualiza la posicion y el estado del selector
    auto getWidestItem() -> int;    // Obtiene la anchura del elemento más ancho del menu
    void checkMenuInput(Menu *menu);  // Gestiona la entrada de teclado y mando durante el menu
    auto findWidth() -> int;        // Calcula el ancho del menu
    auto findHeight() -> int;       // Calcula el alto del menu
    void replaceElementsOnY();      // Recoloca los elementos del menu en el eje Y
    auto getSelectorHeight(int value) -> int;  // Calcula la altura del selector
    void setSelectorItemColors();   // Calcula los colores del selector para el degradado

    // Objetos y punteros
    SDL_Renderer *renderer_;  // Puntero al renderizador de la ventana
    Text *text_;              // Texto para poder escribir los items del menu

    // Variables
    std::string name_;             // Nombre del menu
    int x_;                        // Posición en el eje X de la primera letra del primer elemento
    int y_;                        // Posición en el eje Y de la primera letra del primer elemento
    int h_;                        // Altura del menu
    int w_;                        // Anchura del menu
    int item_selected_;             // Índice del item del menu que ha sido seleccionado
    int default_action_when_cancel_;  // Indice del item del menu que se selecciona cuando se cancela el menu
    int background_type_;           // Tipo de fondo para el menu
    int center_x_;                  // Centro del menu en el eje X
    int center_y_;                  // Centro del menu en el eje Y
    bool is_centered_on_x_;           // Variable para saber si el menu debe estar centrado respecto a un punto en el eje X
    bool is_centered_on_y_;           // Variable para saber si el menu debe estar centrado respecto a un punto en el eje Y
    bool are_elements_centered_on_x_;  // Variable para saber si los elementos van centrados en el eje X
    int widest_item_;               // Anchura del elemento más ancho
    Ja::Sound *sound_accept_;      // Sonido al aceptar o elegir una opción del menu
    Ja::Sound *sound_cancel_;      // Sonido al cancelar el menu
    Ja::Sound *sound_move_;        // Sonido al mover el selector
    Color color_greyed_;            // Color para los elementos agrisados
    Rectangle rect_bg_;             // Rectangulo de fondo del menu
    std::vector<Item> items_;       // Estructura para cada elemento del menu
    Selector selector_;            // Variables para pintar el selector del menu
    std::string font_png_;
    std::string font_txt_;
};
