#pragma once
#include <variant>
#include "GameFixes.h"
#include "GameSettings.h"
#include <imgui.h>

// --- Must be defined before inclusion ---
//struct GameFixes;
extern void SaveSettings();
extern void SetFixesEnabled(GameFixes fix, bool value);

// Checkboxes
struct FixToggle
{
    const char* label;
    bool* value;
    GameFixes   fix;
    const char* tooltip = nullptr;
};

/**
 * @brief Draws an ImGui checkbox to enable or disable a game fix.
 * @param Checkbox A Checkbox structure containing:\n
 *   
 * - label:  The text displayed next to the checkbox.
 * 
 * - value:  Pointer to the boolean controlling the fix state.
 * 
 * - fix:        Identifier of the fix to enable or disable.
 * 
 * - tooltip:    Optional tooltip text shown on hover (may be nullptr).
 * 
 * @note The fix is applied only when the checkbox state is modified by ImGui.
 *       Programmatic changes to the bound value must trigger fix application
 *       logic elsewhere.
 */
static void DrawFixCheckbox(const FixToggle& Checkbox)
{
    if (ImGui::Checkbox(Checkbox.label, Checkbox.value)) {
        if (SetFixesEnabled) SetFixesEnabled(Checkbox.fix, *Checkbox.value);
        SaveSettings();
    }

    if (Checkbox.tooltip && ImGui::IsItemHovered()) {
        ImGui::BeginTooltip();
        ImGui::TextUnformatted(Checkbox.tooltip);
        ImGui::EndTooltip();
    }
}

// Sliders
enum class SliderType
{
    Int,
    Float
};

using ApplyFn = std::variant<
    std::monostate,
    void (*)(int),
    void (*)(float)
>;

struct SliderFix {
    const char* collapsiblelabel;
    const char* label;
    SliderType  type;
    void* value;
    float min;
    float max;
    ApplyFn apply;
    const char* format = nullptr;
    const char* tooltip = nullptr;
};

struct SliderFix2 {
    const char* collapsiblelabel;
    const char* label;
    SliderType  type;
    void* value;
    float min;
    float max;
    GameSetting setting;
    void (*apply)(GameSetting, float); // unique function
    const char* format = nullptr;
    const char* tooltip = nullptr;
};

/**
 * @brief Draws an ImGui slider to adjust a game fix value.
 * @param slider A SliderFix structure containing:\n
 *
 * - collapsiblelabel: Optional label for a collapsible section containing this slider.
 *
 * - label:            The text displayed next to the slider.
 *
 * - type:             Type of slider (e.g., float, int, etc.).
 *
 * - value:            Pointer to the value being modified.
 *
 * - min:              Minimum value of the slider.
 *
 * - max:              Maximum value of the slider.
 *
 * - apply:            Function called when the slider value changes.
 *
 * - format:           Optional format string for displaying the value (eg %.2f or nullptr).
 *
 * - tooltip:          Optional tooltip text shown on hover (may be nullptr).
 *
 * @param width The width of the slider in ImGui units.
 *
 * @note The fix is applied only when the slider value changes via ImGui.
 *       Programmatic changes to the bound value must trigger fix application
 *       logic elsewhere.
 */
static void DrawSlider(const SliderFix& slider, float width)
{
    if (ImGui::CollapsingHeader(slider.collapsiblelabel, ImGuiTreeNodeFlags_DefaultOpen)) {
        ImGui::SetNextItemWidth(width);
        bool changed = false;

        if (slider.type == SliderType::Int) {
            int* v = static_cast<int*>(slider.value);
            changed = ImGui::SliderInt(slider.label, v, static_cast<int>(slider.min), static_cast<int>(slider.max));

            if (changed && std::holds_alternative<void (*)(int)>(slider.apply)) {
                auto fn = std::get<void (*)(int)>(slider.apply);
                fn(*v);
            }
        }
        else {
            float* v = static_cast<float*>(slider.value);
            changed = ImGui::SliderFloat(slider.label, v, slider.min, slider.max, slider.format ? slider.format : "%.2f");

            if (changed && std::holds_alternative<void (*)(float)>(slider.apply)) {
                auto fn = std::get<void (*)(float)>(slider.apply);
                fn(*v);
            }
        }

        if (changed) SaveSettings();

        if (slider.tooltip && ImGui::IsItemHovered()) {
            ImGui::BeginTooltip();
            ImGui::TextUnformatted(slider.tooltip);
            ImGui::EndTooltip();
        }
    }
}

/**
 * @brief Draws an ImGui slider to adjust a game fix value.
 * @param slider A SliderFix structure containing:\n
 *
 * - collapsiblelabel: Optional label for a collapsible section containing this slider.
 *
 * - label:            The text displayed next to the slider.
 *
 * - type:             Type of slider (e.g., float, int, etc.).
 *
 * - value:            Pointer to the value being modified.
 *
 * - min:              Minimum value of the slider.
 *
 * - max:              Maximum value of the slider.
 * 
 * - GameSetting:      Type of slider value to change
 *
 * - apply:            Function called when the slider value changes.
 *
 * - format:           Optional format string for displaying the value (eg %.2f or nullptr).
 *
 * - tooltip:          Optional tooltip text shown on hover (may be nullptr).
 *
 * @param width The width of the slider in ImGui units.
 *
 * @note The fix is applied only when the slider value changes via ImGui.
 *       Programmatic changes to the bound value must trigger fix application
 *       logic elsewhere.
 */
static void DrawSlider2(const SliderFix2& slider, float width)
{
    if (ImGui::CollapsingHeader(slider.collapsiblelabel, ImGuiTreeNodeFlags_DefaultOpen)) {
        ImGui::SetNextItemWidth(width);
        bool changed = false;

        if (slider.type == SliderType::Int) {
            int* v = static_cast<int*>(slider.value);
            changed = ImGui::SliderInt(slider.label, v,
                static_cast<int>(slider.min),
                static_cast<int>(slider.max));

            if (changed && slider.apply)
                slider.apply(slider.setting, static_cast<float>(*v));
        }
        else { // Float
            float* v = static_cast<float*>(slider.value);
            changed = ImGui::SliderFloat(slider.label, v, slider.min, slider.max,
                slider.format ? slider.format : "%.2f");

            if (changed && slider.apply)
                slider.apply(slider.setting, *v);
        }

        if (changed) SaveSettings();

        if (slider.tooltip && ImGui::IsItemHovered()) {
            ImGui::BeginTooltip();
            ImGui::TextUnformatted(slider.tooltip);
            ImGui::EndTooltip();
        }
    }
}
