#include "define_buttons.h"

#include <algorithm>   // Para max, __all_of_fn, all_of
#include <functional>  // Para identity

#include "input.h"     // Para Input, InputAction
#include "lang.h"      // Para getText
#include "options.h"   // Para GamepadOptions, controllers
#include "param.h"     // Para Param, param, ParamGame, ParamTitle
#include "resource.h"  // Para Resource
#include "text.h"      // Para Text

// Constructor
DefineButtons::DefineButtons()
    : input_(Input::get()) {
    // Inicializa variables
    x_ = param.game.width / 2;
    y_ = param.title.press_start_position;

    clearButtons();

    for (int i = 0; i < input_->getNumControllers(); ++i) {
        // controller_names_.emplace_back(input_->getControllerName(i));
    }
}

// Dibuja el objeto en pantalla
void DefineButtons::render() {
    static auto text = Resource::get()->getText("8bithud");
    if (enabled_) {
        // text->writeCentered(x_, y_ - 10, Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(Options::controllers.at(index_controller_).player_id));
        // text->writeCentered(x_, y_, controller_names_.at(index_controller_));
        // text->writeCentered(x_, y_ + 10, buttons_.at(index_button_).label);
    }
}

// Comprueba el botón que se ha pulsado
void DefineButtons::doControllerButtonDown(const SDL_GamepadButtonEvent &event) {
    // Solo pilla botones del mando que toca
    // if (input_->getJoyIndex(event.which) != static_cast<int>(index_controller_)) {
    //    return;
    //}

    const auto BUTTON = static_cast<SDL_GamepadButton>(event.button);
    if (checkButtonNotInUse(BUTTON)) {
        buttons_.at(index_button_).button = BUTTON;
        incIndexButton();
    }
}

// Asigna los botones definidos al input_
void DefineButtons::bindButtons(std::shared_ptr<Input::Gamepad> gamepad) {
    for (const auto &button : buttons_) {
        input_->bindGameControllerButton(gamepad, button.action, button.button);
    }

    // Remapea los inputs a inputs
    input_->bindGameControllerButton(gamepad, Input::Action::SM_SELECT, Input::Action::FIRE_LEFT);
    input_->bindGameControllerButton(gamepad, Input::Action::SM_BACK, Input::Action::FIRE_CENTER);
}

// Comprueba los eventos
void DefineButtons::checkEvents(const SDL_Event &event) {
    if (enabled_) {
        switch (event.type) {
            case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
                doControllerButtonDown(event.gbutton);
                break;
            case SDL_EVENT_GAMEPAD_BUTTON_UP:
                checkEnd();
                break;
            default:
                break;
        }
    }
}

// Habilita el objeto
auto DefineButtons::enable(int index) -> bool {
    if (index < input_->getNumControllers()) {
        enabled_ = true;
        finished_ = false;
        index_button_ = 0;
        clearButtons();
        return true;
    }

    return false;
}

// Incrementa el indice de los botones
void DefineButtons::incIndexButton() {
    if (index_button_ < buttons_.size() - 1) {
        ++index_button_;
    } else {
        finished_ = true;
    }
}

// Guarda los cambios en las opciones
void DefineButtons::saveBindingsToOptions() {
    // auto &controller = Options::controllers.at(index_controller_);
    // controller.name = input_->getControllerName(index_controller_);
    // for (size_t j = 0; j < controller.inputs.size(); ++j) {
    //     controller.buttons.at(j) = input_->getControllerBinding(index_controller_, controller.inputs.at(j));
    // }
}

// Comprueba que un botón no esté ya asignado
auto DefineButtons::checkButtonNotInUse(SDL_GamepadButton button) -> bool {
    return std::ranges::all_of(buttons_, [button](const auto &b) {
        return b.button != button;
    });
}

// Limpia la asignación de botones
void DefineButtons::clearButtons() {
    buttons_.clear();
    buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_LEFT"), Input::Action::FIRE_LEFT, SDL_GAMEPAD_BUTTON_INVALID);
    buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_UP"), Input::Action::FIRE_CENTER, SDL_GAMEPAD_BUTTON_INVALID);
    buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_RIGHT"), Input::Action::FIRE_RIGHT, SDL_GAMEPAD_BUTTON_INVALID);
    buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] START"), Input::Action::START, SDL_GAMEPAD_BUTTON_INVALID);
    buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] SERVICE_MENU"), Input::Action::SERVICE, SDL_GAMEPAD_BUTTON_INVALID);
}

// Comprueba si ha finalizado
void DefineButtons::checkEnd() {
    if (finished_) {
        // bindButtons();               // Asigna los botones definidos al input_
        saveBindingsToOptions();     // Guarda los cambios en las opciones
        input_->resetInputStates();  // Reinicia los estados de las pulsaciones de los botones
        enabled_ = false;            // Deshabilita
    }
}