#include <angelscript.h>
#include "../src/AngelscriptDebugger.hpp"
#include "angelscript_addons/scriptarray/scriptarray.h"
#include "angelscript_addons/scriptbuilder/scriptbuilder.h"
#include "angelscript_addons/scripthelper/scripthelper.h"
#include "angelscript_addons/scriptstdstring/scriptstdstring.h"

static void print(const std::string& s) { std::cout << s << std::endl; }

void MessageCallback(const asSMessageInfo* msg, void* param) {
    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);
}

int main() {
    AngelscriptDebugger debugger;
    debugger.Run(8684);

    std::cout << "Waiting for debugger to attach." << std::endl;
    while (!debugger.HasDebuggerAttached()) {
        usleep(1000);
    }
    std::cout << "Debugger attached." << std::endl;


    asIScriptEngine* engine = asCreateScriptEngine();
    engine->SetMessageCallback(asFUNCTION(MessageCallback), 0, asCALL_CDECL);
    RegisterStdString(engine);
    RegisterScriptArray(engine, true);
    RegisterExceptionRoutines(engine);
    assert(engine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(print), asCALL_CDECL) >= 0);

    CScriptBuilder builder;
    assert(builder.StartNewModule(engine, "TestModule") >= 0);
    assert(builder.AddSectionFromMemory("TestScript", R"(
class TestClass {
    int TestField = 45435;
    TestClass@ RecursiveField;

    TestClass(){
        @RecursiveField = this;
    }

    void main(int a, int b, string d){
        int c = a + b;
        int[] arr = {20, 40, 80, 160};
        print(formatInt(c));
        print(d);
        throw("Error message");
    }
})") >= 0);
    auto buildResult = builder.BuildModule();
    if (buildResult < 0) {
        std::cout << "Failed building module with error code: " << buildResult << std::endl;

        return 1;
    }

    asIScriptModule* mod = engine->GetModule("TestModule");

    asITypeInfo* type = mod->GetTypeInfoByDecl("TestClass");
    asIScriptFunction* factory = type->GetFactoryByDecl("TestClass @TestClass()");

    asIScriptContext* ctx = engine->CreateContext();
    debugger.RegisterContext(ctx);

    ctx->Prepare(factory);
    ctx->Execute();
    asIScriptObject* obj = *(asIScriptObject**)ctx->GetAddressOfReturnValue();
    obj->AddRef();

    asIScriptFunction* func = type->GetMethodByName("main");

    assert(ctx->Prepare(func) == asSUCCESS);
    ctx->SetObject(obj);
    ctx->SetArgDWord(0, 100);
    ctx->SetArgDWord(1, 684);
    std::string s = "foobar";
    ctx->SetArgObject(2, &s);
    ctx->Execute();
    while (ctx->GetState() != asEXECUTION_FINISHED && ctx->GetState() != asEXECUTION_EXCEPTION &&
           ctx->GetState() != asEXECUTION_ABORTED && ctx->GetState() != asEXECUTION_ERROR) {
        usleep(1000);
    }

    ctx->Release();
    obj->Release();
    engine->ShutDownAndRelease();
    debugger.Stop();
}