#ifndef CHAT_COMMAND_HPP_
#define CHAT_COMMAND_HPP_

#include <Entity/PhysicsEntity/CameraEntity/Player/Player.hpp>
#include <UserPermission/UserPermission.hpp>
#include <Utilities/Parse.hpp>
#include <functional>

/**
 * @brief Base class for commands that players can execute via chat.
 */
class ChatCommand
{
private:
    struct Variant
    {
        std::string syntax;
        std::function<bool(Player &player, const std::vector<std::string>&)> execute;
    };

    std::vector<Variant> variants;
    std::vector<UserPermission> requiredPermissions;

protected:
    template<typename... TArgs>
    void addVariant(std::string syntax, std::function<void(Player&, TArgs...)> implCallback)
    {
        variants.push_back(
            {
                syntax,
                [implCallback](Player &player, const std::vector<std::string> &args) -> bool
                {
                    std::tuple<TArgs...> argsTuple;

                    if (!std::apply(
                        [&args](TArgs&... parsedArgs)
                        {
                            return Parse::fromTokens(args.begin() + 1, args.end(), parsedArgs...);
                        },
                        argsTuple
                    ))
                    {
                        return false;
                    }

                    std::apply([&player, implCallback](TArgs... args) { implCallback(player, args...); }, argsTuple);
                    return true;
                }
            }
        );
    }

    template<typename T>
    void addVariant(std::string syntax, T t)
    {
        addVariant(syntax, std::function(t));
    }

    void require(UserPermission perm);

public:
    /**
     * @brief Returns true if the specified player is allowed to use this command.
     */
    bool isAllowedFor(Player &player);

    /**
     * @brief Write the help to the player's chat.
     */
    void showHelp(Player &player, std::string commandName, glm::vec3 color);

    /**
     * @brief Called when a player executes this command.
     * 
     * @param player The player executing this command.
     * @param args The arguments, with `args[0]` being the command itself.
     */
    void execute(Player &player, const std::vector<std::string> &args);
};

#endif