#pragma once

#include <ISXDK.h>
#include <memory>
#include "../isxeq2/ExtensionTLOs.h"

#define ADD_MEMBER_TO_MAP(EnumType, memberName, expectedArgc)                             \
    AddMember(static_cast<DWORD>(EnumType::memberName), const_cast<char *>(#memberName)); \
    memberMap[EnumType::memberName] = [this](int argc, char *argv[], LSOBJECT &dest) {    \
        if (argc != expectedArgc)                                                         \
        {                                                                                 \
            logw << "MrBotApiType::GetMember: " #memberName ": Expected " << expectedArgc \
                 << " argument(s), got " << argc << std::endl;                            \
            return false;                                                                 \
        }                                                                                 \
        log << "MrBotApiType::GetMember: " #memberName << std::endl;                      \
        std::vector<std::string> args(argv, argv + argc);                                 \
        return memberName(args, dest);                                                    \
    }

#define ADD_METHOD_TO_MAP(EnumType, methodName, expectedArgc)                             \
    AddMethod(static_cast<DWORD>(EnumType::methodName), const_cast<char *>(#methodName)); \
    methodMap[EnumType::methodName] = [this](int argc, char *argv[]) {                    \
        if (argc != expectedArgc)                                                         \
        {                                                                                 \
            logw << "MrBotApiType::GetMethod: " #methodName ": Expected " << expectedArgc \
                 << " argument(s), got " << argc << std::endl;                            \
            return false;                                                                 \
        }                                                                                 \
        log << "MrBotApiType::GetMethod: " #methodName << std::endl;                      \
        std::vector<std::string> args(argv, argv + argc);                                 \
        return methodName(args);                                                          \
    }

class MrBotApiType : public LSTypeDefinition
{
public:
    enum class MrBotApiTypeMembers
    {
        Version
    };

    enum class MrBotApiTypeMethods
    {
        ClickActorByName,
        ClickActorById
    };

    MrBotApiType() : LSTypeDefinition(const_cast<char *>("mrbotapi"))
    {
        ADD_MEMBER_TO_MAP(MrBotApiTypeMembers, Version, 0);
        ADD_METHOD_TO_MAP(MrBotApiTypeMethods, ClickActorByName, 1);
        ADD_METHOD_TO_MAP(MrBotApiTypeMethods, ClickActorById, 1);
    }

    virtual bool GetMember(LSOBJECTDATA ObjectData, PLSTYPEMEMBER pMember, int argc, char *argv[], LSOBJECT &Dest);
    virtual bool GetMethod(LSOBJECTDATA &ObjectData, PLSTYPEMEMBER pMethod, int argc, char *argv[]);
    virtual bool ToText(LSOBJECTDATA ObjectData, char *buf, unsigned int buflen);

    static void RegisterType(ISInterface *pISInterface)
    {
        pISInterface->AddLSType(*pMrBotApi);
        pMrBotApi->SetInheritance(nullptr);
    }

    static void RemoveType(ISInterface *pISInterface)
    {
        pISInterface->RemoveLSType(*pMrBotApi);
    }

    static bool __cdecl TLO_MrBotApiType(int argc, char *argv[], LSOBJECT &Dest)
    {
        Dest.DWord = 1;
        Dest.Type = pMrBotApi.get();
        return true;
    }

    static void RegisterTLO(ISInterface *pISInterface)
    {
        pISInterface->AddTopLevelObject(const_cast<char *>("MrBotApi"), MrBotApiType::TLO_MrBotApiType);
    }

    static void RemoveTLO(ISInterface *pISInterface)
    {
        pISInterface->RemoveTopLevelObject(const_cast<char *>("MrBotApi"));
    }

private:
    // Members
    bool Version(const std::vector<std::string> &args, LSOBJECT &dest);
    // Methods
    bool ClickActorByName(const std::vector<std::string> &args);
    bool ClickActorById(const std::vector<std::string> &args);

    static unique_ptr<MrBotApiType> pMrBotApi;
    using MethodFunc = std::function<bool(int argc, char *argv[])>;
    std::map<MrBotApiTypeMethods, MethodFunc> methodMap;
    using MemberFunc = std::function<bool(int argc, char *argv[], LSOBJECT &dest)>;
    std::map<MrBotApiTypeMembers, MemberFunc> memberMap;
};

#undef ADD_MEMBER_TO_MAP
#undef ADD_METHOD_TO_MAP
