#ifdef TESTS_BUILD
#include <doctest.h>
#include "../../src/Battling/Pokemon/CreatePokemon.hpp"
#include "../../src/ScriptResolving/AngelScript/AngelScriptResolver.hpp"
#include "../../src/ScriptResolving/AngelScript/ContextPool.hpp"
#include "../TestLibrary/TestLibrary.hpp"

static std::unordered_map<const char*, const char*> _scripts =
    std::unordered_map<const char*, const char*>{{"basic_ownership_test",
                                                  R"(namespace Pokemon {
    [Pokemon effect="basic_ownership_test"]
    shared class basic_ownership_test : PkmnScript {
        void Test(){
            auto mon = cast<Pokemon@>(GetOwner());
            if (mon is null){
                throw("Owner was null!");
            }
        }
    }
})"},
                                                 {"volatile_test",
                                                  R"(namespace Pokemon {
    [Pokemon effect="volatile_test"]
    shared class volatile_test : PkmnScript {
        void Test(){
            auto mon = cast<Pokemon@>(GetOwner());
            if (mon is null){
                throw("Owner was null!");
            }
        }
    }
})"}};

void MessageCallback(const asSMessageInfo* msg, void*) {
    const char* type = "ERR ";
    if (msg->type == asMSGTYPE_WARNING)
        type = "WARN";
    else if (msg->type == asMSGTYPE_INFORMATION)
        type = "INFO";
    printf("%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message);
}

static AngelScriptResolver* GetScriptResolver(PkmnLib::Battling::BattleLibrary* mainLib) {
    auto res = dynamic_cast<AngelScriptResolver*>(mainLib->GetScriptResolver().get());
    res->Reset(mainLib);
    res->GetEngine()->SetMessageCallback(asFUNCTION(MessageCallback), nullptr, asCALL_CDECL);
    for (auto kv : _scripts) {
        res->CreateScript(kv.first, kv.second);
    }
    res->FinalizeModule();
    return res;
}

static AngelScriptScript* GetScript(PkmnLib::Battling::BattleLibrary* mainLib, const ArbUt::StringView& scriptName,
                                    PkmnLib::Battling::Pokemon* owner) {
    auto* lib = GetScriptResolver(mainLib);
    auto* s = lib->LoadScript(owner, ScriptCategory::Creature, scriptName);
    auto* script = dynamic_cast<AngelScriptScript*>(s);
    REQUIRE(script != nullptr);

    return script;
}

TEST_CASE("Basic script owner tests.") {
    auto lib = TestLibrary::GetLibrary();
    auto mon = PkmnLib::Battling::CreatePokemon(lib, "testSpecies"_cnc, 1).Build();
    auto script = GetScript(lib, "basic_ownership_test"_cnc, mon);
    REQUIRE(script != nullptr);

    script->OnRemove();

    auto ctxPool = script->GetContextPool();
    auto ctx = ctxPool->RequestContext();
    script->PrepareMethod("Test"_cnc, ctx);
    REQUIRE(ctx->Execute() == asEXECUTION_FINISHED);
    ctxPool->ReturnContextToPool(ctx);

    delete script;
    delete mon;
}

TEST_CASE("Add volatile and get owner.") {
    auto lib = TestLibrary::GetLibrary();
    auto mon = PkmnLib::Battling::CreatePokemon(lib, "testSpecies"_cnc, 1).Build();

    auto res = dynamic_cast<AngelScriptResolver*>(lib->GetScriptResolver().get());
    res->Reset(lib);
    for (auto kv : _scripts) {
        res->CreateScript(kv.first, kv.second);
    }
    res->FinalizeModule();
    auto script = (AngelScriptScript*)mon->AddVolatileScript("volatile_test"_cnc);
    REQUIRE(script != nullptr);

    auto owner = script->GetAngelscriptOwner();
    REQUIRE_EQ(owner->GetType(), res->GetEngine()->GetTypeInfoByName("Pokemon"));

    delete mon;
}

#endif