#pragma once

#include <SDL3/SDL.h>

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

#include "utils/utils.h"  // for color_t
class Text;
struct JA_Sound_t;

// 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 {
   private:
    struct rectangle_t {
        SDL_Rect rect;  // Rectangulo
        color_t color;  // Color
        int a;          // Transparencia
    };

    struct item_t {
        std::string label;  // Texto
        SDL_Rect rect;      // Rectangulo que delimita el elemento
        int hPaddingDown;   // Espaciado bajo el elemento
        bool selectable;    // Indica si se puede seleccionar
        bool greyed;        // Indica si ha de aparecer con otro color mas oscuro
        bool linkedDown;    // Indica si el elemento actual y el siguiente se tratan como uno solo. Afecta al selector
        bool linkedUp;      // 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
    };

    struct selector_t {
        float originY;              // Coordenada de origen
        float targetY;              // Coordenada de destino
        float despY;                // 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 originH;              // Altura de origen
        float targetH;              // Altura de destino
        float incH;                 // 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 numJumps;               // Numero de pasos preestablecido para llegar al destino
        int index;                  // Elemento del menu que tiene el foco
        int previousIndex;          // Elemento que tenia el foco previamente
        color_t previousItemColor;  // Color del item nque tenia el foco previamente
        SDL_Rect rect;              // Rectangulo del selector
        color_t color;              // Color del selector
        color_t itemColor;          // Color del item
        color_t jumpItemColors[8];  // Transición de colores para el item seleccionado
        int itemColorIndex;         // Indice del color de transición para el item seleccionado
        int a;                      // Cantidad de transparencia para el rectangulo del selector
    };

    // 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 itemSelected;             // Índice del item del menu que ha sido seleccionado
    int defaultActionWhenCancel;  // Indice del item del menu que se selecciona cuando se cancela el menu
    int backgroundType;           // Tipo de fondo para el menu
    int centerX;                  // Centro del menu en el eje X
    int centerY;                  // Centro del menu en el eje Y
    bool isCenteredOnX;           // Variable para saber si el menu debe estar centrado respecto a un punto en el eje X
    bool isCenteredOnY;           // Variable para saber si el menu debe estar centrado respecto a un punto en el eje Y
    bool areElementsCenteredOnX;  // Variable para saber si los elementos van centrados en el eje X
    int widestItem;               // Anchura del elemento más ancho
    JA_Sound_t *soundAccept;      // Sonido al aceptar o elegir una opción del menu
    JA_Sound_t *soundCancel;      // Sonido al cancelar el menu
    JA_Sound_t *soundMove;        // Sonido al mover el selector
    color_t colorGreyed;          // Color para los elementos agrisados
    rectangle_t rectBG;           // Rectangulo de fondo del menu
    std::vector<item_t> item;     // Estructura para cada elemento del menu
    selector_t selector;          // Variables para pintar el selector del menu
    std::string font_png;
    std::string font_txt;

    // Carga la configuración del menu desde un archivo de texto
    bool load(const std::string &file_path);

    // Parser compartido (recibe cualquier istream)
    bool parseFromStream(std::istream &file, const std::string &filename);

    // Asigna variables a partir de dos cadenas
    bool setVars(const std::string &var, const std::string &value);

    // Asigna variables a partir de dos cadenas
    bool setItem(item_t *item, const std::string &var, const std::string &value);

    // Actualiza el menu para recolocarlo correctamente y establecer el tamaño
    void reorganize();

    // Deja el menu apuntando al siguiente elemento
    void increaseSelectorIndex();

    // Deja el menu apuntando al elemento anterior
    void decreaseSelectorIndex();

    // Actualiza la posicion y el estado del selector
    void updateSelector();

    // Obtiene la anchura del elemento más ancho del menu
    int getWidestItem();

    // Gestiona la entrada de teclado y mando durante el menu
    void checkMenuInput(Menu *menu);

    // Calcula el ancho del menu
    int findWidth();

    // Calcula el alto del menu
    int findHeight();

    // Recoloca los elementos del menu en el eje Y
    void replaceElementsOnY();

    // Calcula la altura del selector
    int getSelectorHeight(int value);

    // Calcula los colores del selector para el degradado
    void setSelectorItemColors();

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

    // Destructor
    ~Menu();

    // Carga el menu desde bytes en memoria
    bool loadFromBytes(const std::vector<uint8_t> &bytes, const std::string &nameForLogs = "");

    // Carga los ficheros de audio
    void loadAudioFile(const std::string &file, int sound);

    // Obtiene el nombre del menu
    const std::string &getName() const;

    // Obtiene el valor de la variable
    int getItemSelected();

    // Deja el menu apuntando al primer elemento
    void reset();

    // Gestiona la entrada de teclado y mando durante el menu
    void checkInput();

    // Actualiza la logica del menu
    void update();

    // Pinta el menu en pantalla
    void render();

    // Establece el color del rectangulo de fondo
    void setBackgroundColor(color_t color, int alpha);

    // Establece el color del rectangulo del selector
    void setSelectorColor(color_t color, int alpha);

    // Establece el color del texto del selector
    void setSelectorTextColor(color_t color);

    // Centra el menu respecto a un punto en el eje X
    void centerMenuOnX(int value = 0);

    // Centra el menu respecto a un punto en el eje Y
    void centerMenuOnY(int value);

    // Centra los elementos del menu en el eje X
    void centerMenuElementsOnX();

    // Añade un item al menu
    void addItem(item_t item);

    // Cambia el texto de un item
    void setItemCaption(int index, const std::string &text);

    // Establece el indice del item que se usará por defecto al cancelar el menu
    void setDefaultActionWhenCancel(int item);

    // Coloca el selector en una posición específica
    void setSelectorPos(int index);

    // Establece el estado seleccionable de un item
    void setSelectable(int index, bool value);

    // Establece el estado agrisado de un item
    void setGreyed(int index, bool value);

    // Establece el estado de enlace de un item
    void setLinkedDown(int index, bool value);

    // Establece el estado de visibilidad de un item
    void setVisible(int index, bool value);

    // Establece el nombre del menu
    void setName(const std::string &name);

    // Establece la posición del menu
    void setPos(int x, int y);

    // Establece el tipo de fondo del menu
    void setBackgroundType(int value);

    // Establece la fuente de texto que se utilizará
    void setText(const std::string &font_png, const std::string &font_txt);

    // Establece el rectangulo de fondo del menu
    void setRectSize(int w = 0, int h = 0);
};
