#ifndef BUILDER_HPP_
#define BUILDER_HPP_

#include <functional>
#include <Widget/Widget.hpp>

/**
 * @brief A widget which re-renders itself based on state.
 */
template<typename TState>
class Builder : public Widget
{
public:
    typedef std::function<Widget*(const TState &state)> BuildCallback;
    typedef std::function<TState()> StateGetter;

private:
    Widget *currentChild;
    TState prevState;

    StateGetter getState;
    BuildCallback build;

    Widget *getChild()
    {
        TState state = getState();
        if (currentChild == nullptr || state != prevState)
        {
            delete currentChild;

            prevState = state;
            currentChild = build(state);

            isRequestingMousePosUpdate = true;
        }

        return currentChild;
    }

public:
    /**
     * @brief Construct the builder.
     * 
     * @param state Reference to the state field.
     * @param callback A function which takes the state and returns the widget.
     */
    Builder(const TState &state, BuildCallback callback) :
        currentChild(nullptr),
        getState([&state]() {return state;}),
        build(callback)
    {}

    Builder(StateGetter getState, BuildCallback build) :
        currentChild(nullptr),
        getState(getState),
        build(build)
    {}

    ~Builder()
    {
        delete currentChild;
    }

    virtual void onResize(glm::ivec2 newSize) override
    {
        Widget::onResize(newSize);
        getChild()->onResize(newSize);
    }

    virtual void draw(GraphicsContext &context) override
    {
        getChild()->widgetAbsPos = widgetAbsPos;
        getChild()->draw(context);
    }

    virtual glm::ivec2 getMinSize() override
    {
        return getChild()->getMinSize();
    }

    virtual void onMouseMoved(glm::ivec2 pos) override
    {
        Widget::onMouseMoved(pos);
        getChild()->onMouseMoved(pos);
    }

    virtual void onMouseUp(Uint8 button) override
    {
        getChild()->onMouseUp(button);
    }

    virtual void onMouseDown(Uint8 button) override
    {
        getChild()->onMouseDown(button);
    }

    virtual void onKeyDown(SDL_Keycode keycode) override
    {
        getChild()->onKeyDown(keycode);
    }

    virtual void onKeyUp(SDL_Keycode keycode) override
    {
        getChild()->onKeyUp(keycode);
    }

    virtual void onScroll(int delta) override
    {
        getChild()->onScroll(delta);
    }

    virtual void getInternalTabOrder(std::vector<Widget*> &tabOrder) override
    {
        getChild()->getInternalTabOrder(tabOrder);
    }
};

#endif