#ifndef ENUM_SELECTOR_HPP_
#define ENUM_SELECTOR_HPP_

#include <Sound/Sound.hpp>
#include <Utilities/StandardLerping.hpp>
#include <Widget/Label/Label.hpp>
#include <Widget/Layout/Column/Column.hpp>
#include <Widget/Layout/MenuButton/MenuButton.hpp>
#include <Widget/Panel/Panel.hpp>
#include <string>
#include <string_view>
#include <unordered_map>
#include <functional>
#include <Widget/Widget.hpp>
#include <Utilities/Utilities.hpp>
#include <Log/Log.hpp>

/**
 * @brief A widget for selecting one of a number of values.
 */
template<typename TEnum>
class EnumSelector : public Widget
{
public:
    struct Option
    {
        TEnum value;
        std::string_view label;
    };

private:
    static constexpr int width = 70;
    static constexpr int height = 13;

    TEnum &value;
    
    std::unordered_map<TEnum, std::string> labels;
    std::vector<Option> options;
    StandardLerping<glm::vec4> backgroundLerp;

    std::function<void()> onValueChanged;

    void activate()
    {
        std::vector<Widget*> menuButtons;
        for (const auto &option : options)
        {
            menuButtons.push_back(
                new MenuButton(
                    std::string(option.label),
                    [this, &option]()
                    {
                        value = option.value;
                        onValueChanged();
                    }
                )
            );
        }
        
        Sound::select.play();

        openPopup(
            glm::ivec2(0, m_size.y),
            new Column(
                new Panel(glm::ivec2(m_size.x - 2, 0)),
                menuButtons
            )
        );
    }

public:
    EnumSelector(
        TEnum &value,
        const std::vector<Option> &options,
        std::function<void()> onValueChanged = [](){}
    ) : value(value),
        options(options),
        onValueChanged(onValueChanged)
    {
        if (options.size() == 0)
        {
            crash("EnumSelector constructed without options.");
        }

        TEnum previous = options.back().value;
        for (const Option &option : options)
        {
            labels[option.value] = option.label;

            previous = option.value;
        }
    }

    virtual glm::ivec2 getMinSize() override
    {
        return glm::ivec2(width, height);
    }

    virtual void draw(GraphicsContext &context) override
    {
        glm::vec4 targetBackground = (isMouseInside() || isFocused() || isHostingPopup()) ?
            Color::selection :
            glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
        context.color = backgroundLerp.sample(targetBackground);
        context.drawRect(0, 0, m_size.x, m_size.y);

        context.color = glm::vec4(1.0f);
        context.drawText(Font::generic, 2, (m_size[1] - 7)/2, labels[value]);
        context.drawMaterial(Material::dropdown, m_size.x - 13, 0);
    }

    virtual void onKeyDown(SDL_Keycode keycode) override
    {
        if (keycode == SDLK_SPACE || keycode == SDLK_RETURN)
        {
            activate();
        }
    }

    virtual void onMouseDown(Uint8 button) override
    {
        Widget::onMouseDown(button);

        if (isMouseInside())
        {
            activate();
        }
    }

    virtual void getInternalTabOrder(std::vector<Widget*> &tabOrder) override
    {
        tabOrder.push_back(this);
    }
};

#endif