#include "game/ui/menu.h"

#include <algorithm>  // for max, min
#include <fstream>    // for char_traits, basic_ifstream, basic_istream
#include <numeric>    // for accumulate
#include <sstream>    // for basic_stringstream

#include "core/audio/audio.hpp"       // for Audio::get (playSound)
#include "core/audio/jail_audio.hpp"  // for JA_LoadSound, JA_DeleteSound (propietat local)
#include "core/input/input.h"         // for Input, REPEAT_FALSE, inputs_e
#include "core/rendering/text.h"      // for Text
#include "core/resources/asset.h"     // for Asset
#include "core/resources/resource_helper.h"

// Constructor
Menu::Menu(SDL_Renderer *renderer, const std::string &file)
    : colorGreyed{128, 128, 128},
      font_png(""),
      font_txt("") {
    // Copia punteros
    this->renderer = renderer;

    // Inicializa punteros
    soundMove = nullptr;
    soundAccept = nullptr;
    soundCancel = nullptr;

    // Inicializa variables
    selector.index = 0;
    selector.previousIndex = 0;
    itemSelected = MENU_NO_OPTION;
    x = 0;
    y = 0;
    w = 0;
    rectBG.rect = {0, 0, 0, 0};
    rectBG.color = {0, 0, 0};
    rectBG.a = 0;
    backgroundType = MENU_BACKGROUND_SOLID;
    isCenteredOnX = false;
    isCenteredOnY = false;
    areElementsCenteredOnX = false;
    centerX = 0;
    centerY = 0;
    widestItem = 0;
    defaultActionWhenCancel = 0;

    // Selector
    selector.originY = 0;
    selector.targetY = 0;
    selector.despY = 0;
    selector.originH = 0;
    selector.targetH = 0;
    selector.incH = 0;
    selector.y = 0.0f;
    selector.h = 0.0f;
    selector.numJumps = 8;
    selector.moving = false;
    selector.resizing = false;
    selector.rect = {0, 0, 0, 0};
    selector.color = {0, 0, 0};
    selector.itemColor = {0, 0, 0};
    selector.a = 255;

    // Inicializa las variables desde un fichero. Si no se pasa fichero, el
    // llamante (p.ej. Resource::preloadAll) usará loadFromBytes después —
    // y ese método ya llama a setSelectorItemColors() y reset() al final.
    if (!file.empty()) {
        load(file);
        setSelectorItemColors();
        reset();
    }
}

Menu::~Menu() {
    renderer = nullptr;

    if (soundMove) {
        JA_DeleteSound(soundMove);
    }

    if (soundAccept) {
        JA_DeleteSound(soundAccept);
    }

    if (soundCancel) {
        JA_DeleteSound(soundCancel);
    }

    if (text != nullptr) {
        delete text;
    }
}

// Parser compartido (recibe cualquier istream)
bool Menu::parseFromStream(std::istream &file, const std::string &filename) {
    bool success = true;
    bool textAllocated = false;
    std::string line;
    (void)filename;

    while (std::getline(file, line)) {
        if (line == "[item]") {
            item_t newItem;
            newItem.label = "";
            newItem.hPaddingDown = 1;
            newItem.selectable = true;
            newItem.greyed = false;
            newItem.linkedDown = false;
            newItem.visible = true;
            newItem.line = false;

            do {
                std::getline(file, line);
                int pos = line.find("=");
                if (!setItem(&newItem, line.substr(0, pos), line.substr(pos + 1, line.length()))) {
                    success = false;
                }
            } while (line != "[/item]");

            addItem(newItem);
        } else {
            int pos = line.find("=");
            if (!setVars(line.substr(0, pos), line.substr(pos + 1, line.length()))) {
                success = false;
            }

            // Crea el objeto text tan pronto como se pueda. Necesario para añadir items.
            // Carga via ResourceHelper para que funcione tanto con pack como con filesystem.
            if (font_png != "" && font_txt != "" && !textAllocated) {
                auto pngBytes = ResourceHelper::loadFile(Asset::get()->get(font_png));
                auto txtBytes = ResourceHelper::loadFile(Asset::get()->get(font_txt));
                text = new Text(pngBytes, txtBytes, renderer);
                textAllocated = true;
            }
        }
    }
    return success;
}

// Carga la configuración del menu desde un archivo de texto
bool Menu::load(const std::string &file_path) {
    const std::string filename = file_path.substr(file_path.find_last_of("\\/") + 1);
    std::ifstream file(file_path);
    if (!file.good()) {
        return false;
    }
    return parseFromStream(file, filename);
}

// Carga el menu desde bytes en memoria
bool Menu::loadFromBytes(const std::vector<uint8_t> &bytes, const std::string &nameForLogs) {
    if (bytes.empty()) return false;
    std::string content(reinterpret_cast<const char *>(bytes.data()), bytes.size());
    std::stringstream ss(content);
    bool ok = parseFromStream(ss, nameForLogs);
    setSelectorItemColors();
    reset();
    return ok;
}

// Asigna variables a partir de dos cadenas
bool Menu::setItem(item_t *item, const std::string &var, const std::string &value) {
    // Indicador de éxito en la asignación
    bool success = true;

    if (var == "text") {
        item->label = value;
    }

    else if (var == "hPaddingDown") {
        item->hPaddingDown = std::stoi(value);
    }

    else if (var == "selectable") {
        item->selectable = value == "true" ? true : false;
    }

    else if (var == "greyed") {
        item->greyed = value == "true" ? true : false;
    }

    else if (var == "linkedDown") {
        item->linkedDown = value == "true" ? true : false;
    }

    else if (var == "visible") {
        item->visible = value == "true" ? true : false;
    }

    else if (var == "line") {
        item->line = value == "true" ? true : false;
    }

    else if ((var == "") || (var == "[/item]")) {
    }

    else {
        success = false;
    }

    return success;
}

// Asigna variables a partir de dos cadenas
bool Menu::setVars(const std::string &var, const std::string &value) {
    // Indicador de éxito en la asignación
    bool success = true;

    if (var == "font_png") {
        font_png = value;
    }

    else if (var == "font_txt") {
        font_txt = value;
    }

    else if (var == "sound_cancel") {
        auto bytes = ResourceHelper::loadFile(Asset::get()->get(value));
        if (!bytes.empty()) soundCancel = JA_LoadSound(bytes.data(), (uint32_t)bytes.size());
    }

    else if (var == "sound_accept") {
        auto bytes = ResourceHelper::loadFile(Asset::get()->get(value));
        if (!bytes.empty()) soundAccept = JA_LoadSound(bytes.data(), (uint32_t)bytes.size());
    }

    else if (var == "sound_move") {
        auto bytes = ResourceHelper::loadFile(Asset::get()->get(value));
        if (!bytes.empty()) soundMove = JA_LoadSound(bytes.data(), (uint32_t)bytes.size());
    }

    else if (var == "name") {
        name = value;
    }

    else if (var == "x") {
        x = std::stoi(value);
    }

    else if (var == "centerX") {
        centerX = std::stoi(value);
    }

    else if (var == "centerY") {
        centerY = std::stoi(value);
    }

    else if (var == "y") {
        y = std::stoi(value);
    }

    else if (var == "backgroundType") {
        backgroundType = std::stoi(value);
    }

    else if (var == "backgroundColor") {
        // Se introducen los valores separados por comas en un vector
        std::stringstream ss(value);
        std::string tmp;
        getline(ss, tmp, ',');
        rectBG.color.r = std::stoi(tmp);
        getline(ss, tmp, ',');
        rectBG.color.g = std::stoi(tmp);
        getline(ss, tmp, ',');
        rectBG.color.b = std::stoi(tmp);
        getline(ss, tmp, ',');
        rectBG.a = std::stoi(tmp);
    }

    else if (var == "selector_color") {
        // Se introducen los valores separados por comas en un vector
        std::stringstream ss(value);
        std::string tmp;
        getline(ss, tmp, ',');
        selector.color.r = std::stoi(tmp);
        getline(ss, tmp, ',');
        selector.color.g = std::stoi(tmp);
        getline(ss, tmp, ',');
        selector.color.b = std::stoi(tmp);
        getline(ss, tmp, ',');
        selector.a = std::stoi(tmp);
    }

    else if (var == "selector_text_color") {
        // Se introducen los valores separados por comas en un vector
        std::stringstream ss(value);
        std::string tmp;
        getline(ss, tmp, ',');
        selector.itemColor.r = std::stoi(tmp);
        getline(ss, tmp, ',');
        selector.itemColor.g = std::stoi(tmp);
        getline(ss, tmp, ',');
        selector.itemColor.b = std::stoi(tmp);
    }

    else if (var == "areElementsCenteredOnX") {
        areElementsCenteredOnX = value == "true" ? true : false;
    }

    else if (var == "isCenteredOnX") {
        isCenteredOnX = value == "true" ? true : false;
    }

    else if (var == "isCenteredOnY") {
        isCenteredOnY = value == "true" ? true : false;
    }

    else if (var == "defaultActionWhenCancel") {
        defaultActionWhenCancel = std::stoi(value);
    }

    else if (var == "") {
    }

    else {
        success = false;
    }

    return success;
}

// Carga los ficheros de audio
void Menu::loadAudioFile(const std::string &file, int sound) {
    switch (sound) {
        case SOUND_ACCEPT:
            soundAccept = JA_LoadSound(file.c_str());
            break;

        case SOUND_CANCEL:
            soundCancel = JA_LoadSound(file.c_str());
            break;

        case SOUND_MOVE:
            soundMove = JA_LoadSound(file.c_str());
            break;

        default:
            break;
    }
}

// Obtiene el nombre del menu
const std::string &Menu::getName() const {
    return name;
}

// Obtiene el valor de la variable
int Menu::getItemSelected() {
    // Al llamar a esta funcion, se obtiene el valor y se borra
    const int temp = itemSelected;
    itemSelected = MENU_NO_OPTION;
    return temp;
}

// Actualiza la posicion y el estado del selector
void Menu::updateSelector() {
    if (selector.moving) {
        // Calcula el desplazamiento en Y
        selector.y += selector.despY;
        if (selector.despY > 0)  // Va hacia abajo
        {
            if (selector.y > selector.targetY)  // Ha llegado al destino
            {
                selector.originY = selector.y = selector.targetY;
                selector.moving = false;
            }
        }

        else if (selector.despY < 0)  // Va hacia arriba
        {
            if (selector.y < selector.targetY)  // Ha llegado al destino
            {
                selector.originY = selector.y = selector.targetY;
                selector.moving = false;
            }
        }
        selector.rect.y = int(selector.y);

        // Actualiza el color del item
        selector.itemColorIndex++;
        selector.itemColorIndex = std::min(selector.numJumps - 1, selector.itemColorIndex);
        selector.itemColor = selector.jumpItemColors[selector.itemColorIndex];
        selector.previousItemColor = selector.jumpItemColors[selector.numJumps - 1 - selector.itemColorIndex];
    } else {
        selector.rect.y = int(selector.y);
        selector.itemColorIndex = 0;
        selector.itemColor = selector.jumpItemColors[selector.numJumps - 1];
        selector.previousItemColor = selector.jumpItemColors[0];
    }

    if (selector.resizing) {
        // Calcula el incremento en H
        selector.h += selector.incH;
        if (selector.incH > 0)  // Crece
        {
            if (selector.h > selector.targetH)  // Ha llegado al destino
            {
                // selector.originH = selector.targetH = selector.rect.h = getSelectorHeight(selector.index);
                selector.originH = selector.h = selector.targetH;
                selector.resizing = false;
            }
        }

        else if (selector.incH < 0)  // Decrece
        {
            if (selector.h < selector.targetH)  // Ha llegado al destino
            {
                // selector.originH = selector.targetH = selector.rect.h = getSelectorHeight(selector.index);
                selector.originH = selector.h = selector.targetH;
                selector.resizing = false;
            }
        }
        selector.rect.h = int(selector.h);
    } else {
        selector.rect.h = getSelectorHeight(selector.index);
    }
}

// Coloca el selector en una posición específica
void Menu::setSelectorPos(int index) {
    if (index < (int)item.size()) {
        selector.index = index;
        selector.rect.y = selector.y = selector.originY = selector.targetY = item[selector.index].rect.y;
        selector.rect.w = rectBG.rect.w;
        selector.rect.x = rectBG.rect.x;
        selector.originH = selector.targetH = selector.rect.h = getSelectorHeight(selector.index);
        selector.moving = false;
        selector.resizing = false;
    }
}

// Obtiene la anchura del elemento más ancho del menu
int Menu::getWidestItem() {
    return std::accumulate(item.begin(), item.end(), 0, [](int acc, const item_t &i) { return std::max(acc, i.rect.w); });
}

// Deja el menu apuntando al primer elemento
void Menu::reset() {
    itemSelected = MENU_NO_OPTION;
    selector.index = 0;
    selector.originY = selector.targetY = selector.y = item[0].rect.y;
    selector.originH = selector.targetH = item[0].rect.h;
    selector.moving = false;
    selector.resizing = false;

    // Si el primer elemento no es seleccionable, incrementa el selector
    if (!item[selector.index].selectable) {
        increaseSelectorIndex();
        setSelectorPos(selector.index);
    }
}

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

    if (isCenteredOnX) {
        centerMenuOnX(centerX);
    }

    if (isCenteredOnY) {
        centerMenuOnY(centerY);
    }

    if (areElementsCenteredOnX) {
        centerMenuElementsOnX();
    }
}

// Deja el menu apuntando al siguiente elemento
void Menu::increaseSelectorIndex() {
    // Guarda el indice actual antes de modificarlo
    selector.previousIndex = selector.index;

    // Obten las coordenadas del elemento actual
    selector.y = selector.originY = item[selector.index].rect.y;
    selector.h = selector.originH = getSelectorHeight(selector.index);

    // Calcula cual es el siguiente elemento
    ++selector.index %= item.size();
    while (!item[selector.index].selectable) {
        ++selector.index %= item.size();
    }

    // Establece las coordenadas y altura de destino
    selector.targetY = item[selector.index].rect.y;
    selector.despY = (selector.targetY - selector.originY) / selector.numJumps;

    selector.targetH = getSelectorHeight(selector.index);
    selector.incH = (selector.targetH - selector.originH) / selector.numJumps;

    selector.moving = true;
    if (selector.incH != 0) {
        selector.resizing = true;
    }
}

// Deja el menu apuntando al elemento anterior
void Menu::decreaseSelectorIndex() {
    // Guarda el indice actual antes de modificarlo
    selector.previousIndex = selector.index;

    // Obten las coordenadas del elemento actual
    selector.y = selector.originY = item[selector.index].rect.y;
    selector.h = selector.originH = getSelectorHeight(selector.index);

    // Calcula cual es el siguiente elemento
    if (selector.index == 0) {
        selector.index = item.size() - 1;
    } else {
        selector.index--;
    }

    while (!item[selector.index].selectable) {
        if (selector.index == 0) {
            selector.index = item.size() - 1;
        } else {
            selector.index--;
        }
    }

    // Establece las coordenadas y altura de destino
    selector.targetY = item[selector.index].rect.y;
    selector.despY = (selector.targetY - selector.originY) / selector.numJumps;

    selector.targetH = getSelectorHeight(selector.index);
    selector.incH = (selector.targetH - selector.originH) / selector.numJumps;

    selector.moving = true;
    if (selector.incH != 0) {
        selector.resizing = true;
    }
}

// Actualiza la logica del menu
void Menu::update() {
    checkInput();
    updateSelector();
}

// Pinta el menu en pantalla
void Menu::render() {
    // Rendereritza el fondo del menu
    if (backgroundType == MENU_BACKGROUND_SOLID) {
        SDL_FRect fBG = {(float)rectBG.rect.x, (float)rectBG.rect.y, (float)rectBG.rect.w, (float)rectBG.rect.h};
        SDL_SetRenderDrawColor(renderer, rectBG.color.r, rectBG.color.g, rectBG.color.b, rectBG.a);
        SDL_RenderFillRect(renderer, &fBG);
    }

    // Renderiza el rectangulo del selector
    const SDL_FRect fTemp = {(float)selector.rect.x, (float)(selector.rect.y - 1), (float)selector.rect.w, (float)(selector.rect.h + 1)};
    SDL_SetRenderDrawColor(renderer, selector.color.r, selector.color.g, selector.color.b, selector.a);
    SDL_RenderFillRect(renderer, &fTemp);

    // Renderiza el borde del fondo
    if (backgroundType == MENU_BACKGROUND_SOLID) {
        SDL_FRect fBGBorder = {(float)rectBG.rect.x, (float)rectBG.rect.y, (float)rectBG.rect.w, (float)rectBG.rect.h};
        SDL_SetRenderDrawColor(renderer, rectBG.color.r, rectBG.color.g, rectBG.color.b, 255);
        SDL_RenderRect(renderer, &fBGBorder);
    }

    // Crea una linea por si hay que dibujarla entre los items
    h_line_t line;
    line.x1 = selector.rect.x + (selector.rect.w / 6);
    line.x2 = line.x1 + ((selector.rect.w / 6) * 4);

    // Renderiza el texto
    for (int i = 0; i < (int)item.size(); ++i) {
        if (item[i].visible) {
            // Comprueba si ha de dibujar una linea en el elemento del menu
            if (item[i].line) {
                line.y = item[i].rect.y + item[i].rect.h + (item[i].hPaddingDown / 2) - 1;
                SDL_SetRenderDrawColor(renderer, 255, 255, 255, 64);
                SDL_RenderLine(renderer, line.x1, line.y, line.x2, line.y);
            }

            // Dibuja el elemento
            if (item[i].greyed) {  // Tiene prioridad si el elemento es gris
                text->writeColored(item[i].rect.x, item[i].rect.y, item[i].label, colorGreyed);
            }

            else if (i == selector.index) {  // A continuación si tiene el indice
                const color_t color = {selector.itemColor.r, selector.itemColor.g, selector.itemColor.b};
                text->writeColored(item[i].rect.x, item[i].rect.y, item[i].label, color);
            }

            else if (i == selector.previousIndex) {  // O si lo ha tenido
                const color_t color = {selector.previousItemColor.r, selector.previousItemColor.g, selector.previousItemColor.b};
                text->writeColored(item[i].rect.x, item[i].rect.y, item[i].label, color);
            }

            else if (item[i].selectable) {  // O si simplemente es un elemento normal
                text->write(item[i].rect.x, item[i].rect.y, item[i].label);
            }

            else {                                                      // Si no es seleccionable
                if ((item[i].linkedUp) && (i == selector.index + 1)) {  // Si el elemento está enlazado con el elemento superior se pinta del color del selector
                    const color_t color = {selector.itemColor.r, selector.itemColor.g, selector.itemColor.b};
                    text->writeColored(item[i].rect.x, item[i].rect.y, item[i].label, color);
                } else {  // Si no está enlazado con el elemento superior se pinta con el color normal
                    text->write(item[i].rect.x, item[i].rect.y, item[i].label);
                }
            }
        }
    }
}

// Establece el rectangulo de fondo del menu y el selector
void Menu::setRectSize(int w, int h) {
    // Establece el ancho
    if (w == 0) {            // Si no se pasa un valor, se busca si hay uno prefijado
        if (this->w == 0) {  // Si no hay prefijado, coge el item mas ancho
            rectBG.rect.w = findWidth() + text->getCharacterSize();
        } else {  // Si hay prefijado, coge ese
            rectBG.rect.w = this->w;
        }
    } else {  // Si se pasa un valor, se usa y se prefija
        rectBG.rect.w = w;
        this->w = w;
    }

    // Establece el alto
    if (h == 0) {  // Si no se pasa un valor, se busca de manera automatica
        rectBG.rect.h = findHeight() + text->getCharacterSize();
    } else {  // Si se pasa un valor, se aplica
        rectBG.rect.h = h;
    }

    // La posición X es la del menú menos medio caracter
    if (this->w != 0) {  // Si el ancho esta prefijado, la x coinccide
        rectBG.rect.x = x;
    } else {  // Si el ancho es automatico, se le da un poco de margen
        rectBG.rect.x = x - (text->getCharacterSize() / 2);
    }

    // La posición Y es la del menu menos la altura de medio caracter
    rectBG.rect.y = y - (text->getCharacterSize() / 2);

    // Establecemos los valores del rectangulo del selector a partir de los valores del rectangulo de fondo
    setSelectorPos(selector.index);
}

// Establece el color del rectangulo de fondo
void Menu::setBackgroundColor(color_t color, int alpha) {
    rectBG.color = color;
    rectBG.a = alpha;
}

// Establece el color del rectangulo del selector
void Menu::setSelectorColor(color_t color, int alpha) {
    selector.color = color;
    selector.a = alpha;
}

// Establece el color del texto del selector
void Menu::setSelectorTextColor(color_t color) {
    selector.itemColor = color;
}

// Centra el menu respecto un punto en el eje X
void Menu::centerMenuOnX(int value) {
    isCenteredOnX = true;
    if (value != 0) {
        centerX = value;
    } else if (centerX == 0) {
        return;
    }

    // Establece la nueva posición centrada en funcion del elemento más ancho o del ancho fijo del menu
    if (w != 0) {  // Si se ha definido un ancho fijo
        x = (centerX) - (w / 2);
    } else {  // Si se actua en función del elemento más ancho
        x = (centerX) - (findWidth() / 2);
    }

    // Actualiza el rectangulo de fondo y del selector
    rectBG.rect.x = x;
    selector.rect.x = x;

    // Reposiciona los elementos del menu
    for (auto &i : item) {
        i.rect.x = x;
    }

    // Recalcula el rectangulo de fondo
    setRectSize();

    // Vuelve a centrar los elementos si fuera el caso
    if (areElementsCenteredOnX) {
        centerMenuElementsOnX();
    }
}

// Centra el menu respecto un punto en el eje Y
void Menu::centerMenuOnY(int value) {
    isCenteredOnY = true;
    centerY = value;

    // Establece la nueva posición centrada en funcion del elemento más ancho
    y = (value) - (findHeight() / 2);

    // Reposiciona los elementos del menu
    replaceElementsOnY();

    // Recalcula el rectangulo de fondo
    setRectSize();
}

// Centra los elementos del menu en el eje X
void Menu::centerMenuElementsOnX() {
    areElementsCenteredOnX = true;

    for (auto &i : item) {
        i.rect.x = (centerX - (i.rect.w / 2));
    }
}

// Añade un item al menu
void Menu::addItem(item_t temp) {
    // item_t temp;

    if (item.empty()) {  // Si es el primer item coge la posición en el eje Y del propio menu
        temp.rect.y = y;
    } else {  // En caso contrario, coge la posición en el eje Y a partir del último elemento
        temp.rect.y = item.back().rect.y + item.back().rect.h + item.back().hPaddingDown;
    }

    temp.rect.x = x;

    item.push_back(temp);

    setItemCaption(item.size() - 1, temp.label);

    if (item.size() > 1) {
        if (item[item.size() - 2].linkedDown) {
            item.back().linkedUp = true;
        }
    }

    centerX = x + (findWidth() / 2);
    reorganize();
}

// Cambia el texto de un item
void Menu::setItemCaption(int index, const std::string &text) {
    item[index].label = text;
    item[index].rect.w = this->text->lenght(item[index].label);
    item[index].rect.h = this->text->getCharacterSize();
    reorganize();
}

// Establece el indice del itemm que se usará por defecto al cancelar el menu
void Menu::setDefaultActionWhenCancel(int item) {
    defaultActionWhenCancel = item;
}

// Gestiona la entrada de teclado y mando durante el menu
void Menu::checkInput() {
    if (Input::get()->checkInput(input_up, REPEAT_FALSE)) {
        decreaseSelectorIndex();
        if (soundMove) {
            Audio::get()->playSound(soundMove);
        }
    }

    if (Input::get()->checkInput(input_down, REPEAT_FALSE)) {
        increaseSelectorIndex();
        if (soundMove) {
            Audio::get()->playSound(soundMove);
        }
    }

    if (Input::get()->checkInput(input_accept, REPEAT_FALSE)) {
        itemSelected = selector.index;
        if (soundAccept) {
            Audio::get()->playSound(soundAccept);
        }
    }

    if (Input::get()->checkInput(input_cancel, REPEAT_FALSE)) {
        itemSelected = defaultActionWhenCancel;
        if (soundCancel) {
            Audio::get()->playSound(soundCancel);
        }
    }
}

// Calcula el ancho del menu
int Menu::findWidth() {
    return getWidestItem();
}

// Calcula el alto del menu
int Menu::findHeight() {
    const int height = std::accumulate(item.begin(), item.end(), 0, [](int acc, const item_t &i) { return acc + i.rect.h + i.hPaddingDown; });

    return height - item.back().hPaddingDown;
}

// Recoloca los elementos del menu en el eje Y
void Menu::replaceElementsOnY() {
    item[0].rect.y = y;

    for (int i = 1; i < (int)item.size(); i++) {
        item[i].rect.y = item[i - 1].rect.y + item[i - 1].rect.h + item[i - 1].hPaddingDown;
    }
}

// Establece el estado seleccionable de un item
void Menu::setSelectable(int index, bool value) {
    item[index].selectable = value;
}

// Establece el estado agrisado de un item
void Menu::setGreyed(int index, bool value) {
    item[index].greyed = value;
}

// Establece el estado de enlace de un item
void Menu::setLinkedDown(int index, bool value) {
    item[index].linkedDown = value;
}

// Establece el estado de visibilidad de un item
void Menu::setVisible(int index, bool value) {
    item[index].visible = value;
}

// Calcula la altura del selector
int Menu::getSelectorHeight(int value) {
    if (item[value].linkedDown) {
        return item[value].rect.h + item[value].hPaddingDown + item[value + 1].rect.h;
    } else {
        return item[value].rect.h;
    }
}

// Establece el nombre del menu
void Menu::setName(const std::string &name) {
    this->name = name;
}

// Establece la posición del menu
void Menu::setPos(int x, int y) {
    this->x = x;
    this->y = y;
}

// Establece el tipo de fondo del menu
void Menu::setBackgroundType(int value) {
    backgroundType = value;
}

// Establece la fuente de texto que se utilizará
void Menu::setText(const std::string &font_png, const std::string &font_txt) {
    if (!text) {
        text = new Text(Asset::get()->get(font_png), Asset::get()->get(font_txt), renderer);
    }
}

// Calcula los colores del selector para el degradado
void Menu::setSelectorItemColors() {
    const color_t colorFrom = {255, 255, 255};
    const color_t colorTo = selector.itemColor;

    for (int i = 0; i < selector.numJumps; ++i) {
        const float step = ((float)i / (selector.numJumps - 1));
        const int r = colorFrom.r + ((colorTo.r - colorFrom.r) * step);
        const int g = colorFrom.g + ((colorTo.g - colorFrom.g) * step);
        const int b = colorFrom.b + ((colorTo.b - colorFrom.b) * step);
        selector.jumpItemColors[i].r = r;
        selector.jumpItemColors[i].g = g;
        selector.jumpItemColors[i].b = b;
    }

    selector.itemColorIndex = 0;
}