#ifndef PLAYERACTIONMODULE_H
#define PLAYERACTIONMODULE_H
#include <flecs.h>
#include <nlohmann/json.hpp>
#include <lua.h>
#include <Ogre.h>

namespace ECS {

struct ActionNodeList {
    struct indexObject;
    struct ActionNode {
        Ogre::String action;
        Ogre::String action_text;
        Ogre::Vector3 position;
        Ogre::Quaternion rotation;
        float height;
        float radius;
        bool dynamic;
        nlohmann::json props;
    };
    struct UIData {
        std::shared_ptr<std::mutex> mutex;
        int selected;
        std::vector<size_t> points;
        std::vector<float> distances;
        UIData()
            : mutex(std::make_shared<std::mutex>())
            , selected(-1)
        {
        }
    };

private:
    bool dirty;
    bool busy;
    std::shared_ptr<indexObject> indexObj;
    struct UIData uidata;
    bool _query(const Ogre::Vector3 &position, std::vector<size_t> &points,
           std::vector<float> &distances);

public:
    std::shared_ptr<std::mutex> nodeMutex;
    std::vector<ActionNode> nodes, dynamicNodes;
    void updateDynamicNodes();
    void build();
    bool query_ai(const Ogre::Vector3 &position, float distance,
              std::vector<size_t> &points,
              std::vector<float> &distances);
    int addNode(struct ActionNodeList::ActionNode &node);
    void removeNode(int index);
    const UIData &getUIData();
    void setUISelected(int selected);
    void setUIPoints(const std::vector<size_t> &points,
             const std::vector<float> &distances);
    void UIquery(const Ogre::Vector3 &position);
    void setDirty(); // node was added or removed
    void setReady();
    void setBusy();
    bool isBusy();
};
struct PlayerActionModule {
    struct ActionWordHandler {
        /** actor is -1 for player >=0 for NPCs */
        virtual void operator()(int actor, flecs::entity town, int index,
                    const Ogre::String &word, int actionNode) = 0;
    };
    struct ActivatedWordHandler {
        enum {OK = 0, BUSY, ERROR};
    private:
        bool active;
    public:
        ActivatedWordHandler(): active(false) {}
        int _update(float delta)
        {
            if (!active) {
                enter();
                active = true;
            }
            int ret = update(delta);
            if (ret != BUSY) {
                exit(ret);
                active = false;
            }
            return ret;
        }
        virtual ~ActivatedWordHandler() {}
        bool _is_active() {return active;}
    private:
        virtual void enter() = 0;
        virtual void exit(int result) = 0;
        virtual int update(float delta) = 0;
    };
    std::multimap<Ogre::String, ActionWordHandler *>
        actionWords;
    std::multimap<Ogre::String, ActivatedWordHandler *> activatedWords;

    PlayerActionModule(flecs::world &ecs);
    void addWordHandler(const Ogre::String &word, ActionWordHandler *handler);
    void removeWordHandler(const Ogre::String &word, ActionWordHandler *handler);
    void addLuaWordHandler(const Ogre::String &word, lua_State *L, int ref);
    void removeLuaWordHandler(const Ogre::String &word, lua_State *L, int ref);
    int setupLuaActionHandler(lua_State *L);
    void addActivatedWordHandler(const Ogre::String &word, ActivatedWordHandler *handler);
    void removeActivatedWordHandler(const Ogre::String &word, ActivatedWordHandler *handler);
};
}

#endif // PLAYERACTIONMODULE_H
