#include "Database.hpp"
#include <sstream>

Napi::Object Database::Init(Napi::Env env, Napi::Object exports) {
    Napi::Function func =
        DefineClass(env, "Database",
                    {
                        InstanceMethod("reset", &Database::Reset),
                        InstanceMethod("loadScript", &Database::LoadScript),
                        InstanceMethod("build", &Database::Build),
                     InstanceMethod("messages", &Database::GetMessages)});

    auto* constructor = new Napi::FunctionReference();
    *constructor = Napi::Persistent(func);

    env.SetInstanceData(constructor);
    exports.Set("Database", func);
    return exports;
}

void Database::MessageCallback(const asSMessageInfo* msg) {
    _messages.push_back(new Diagnostic(msg->section, msg->row, msg->col, msg->type, msg->message));
}

Database::Database(const Napi::CallbackInfo& info) : ObjectWrap(info) {
    _engine = asCreateScriptEngine();
    _engine->SetMessageCallback(asMETHOD(Database, MessageCallback), this, asCALL_THISCALL);
    Reset(info);
}

void Database::Reset(const Napi::CallbackInfo&) {
    _engine->DiscardModule("Module");
    _builder.StartNewModule(_engine, "Module");
}

void Database::LoadScript(const Napi::CallbackInfo& info) {
    if (info.Length() < 2)
        throw std::logic_error("Not enough arguments");
    auto name = info[0].As<Napi::String>().Utf8Value();
    auto script = info[1].As<Napi::String>().Utf8Value();

    _builder.AddSectionFromMemory(name.c_str(), script.c_str());
}
Napi::Value Database::Build(const Napi::CallbackInfo& info) {
    _messages.clear();
    _builder.BuildModule();
    return Napi::Number::New(info.Env(), 0);
}
Napi::Value Database::GetMessages(const Napi::CallbackInfo& info) {
    auto messages = Napi::Array::New(info.Env());
    for (auto msg : _messages) {
        auto obj = Napi::Object::New(info.Env());
        obj.Set("section", Napi::String::New(info.Env(), msg->GetSection()));
        obj.Set("row", Napi::Number::New(info.Env(), msg->GetRow()));
        obj.Set("col", Napi::Number::New(info.Env(), msg->GetCol()));
        obj.Set("type", Napi::Number::New(info.Env(), msg->GetType()));
        obj.Set("message", Napi::String::New(info.Env(), msg->GetMessage()));
        messages[messages.Length()] = obj;
    }

    return messages;
}
