#include "ScriptCore.h"
#include "../../core/utils/Logging.h"
#include "../../core/utils/ExceptionHandler.h"
#include "../../core/utils/Profiler.h"
#include "../../Components/ScriptComponent.h"

void ScriptCore::Init()
{
    s_LuaStates.clear();
    s_LuaStates.reserve(5);
    Logger::LogOk("Script Core");
}

void ScriptCore::RegisterState(lua_State* L,
                               const std::string& scriptPath,
                               const std::string& name,
                               ScriptComponent* component)
{
    s_LuaStates.push_back({ L, scriptPath, name, component });
    
    

    Logger::LogVerbose("[ScriptCore] Registered Script Instance '%s'", name.c_str());
}

void ScriptCore::UnregisterState(lua_State* L)
{
    auto it = std::remove_if(s_LuaStates.begin(), s_LuaStates.end(),
                             [L](const LuaScript& script)
                             {
                                 return script.L == L;
                             });

    if (it != s_LuaStates.end())
    {
        Logger::LogVerbose("[ScriptCore] Unregistered Lua state");
        s_LuaStates.erase(it, s_LuaStates.end());
    }
    else
    {
        Logger::LogWarning("[ScriptCore] Tried to unregister unknown Lua state");
    }
}

void ScriptCore::CallAllInits()
{
    for (auto& script : s_LuaStates)
    {
        PROFILE_DEEP_SCOPE("ScriptCore::OnInit");

        if (!script.L || !script.component)
            continue;

        script.component->ReloadScript();

        lua_getglobal(script.L, "OnInit");
        if (lua_isfunction(script.L, -1))
        {
            if (lua_pcall(script.L, 0, 0, 0) != LUA_OK)
            {
                Logger::LogError("[Lua] %s", lua_tostring(script.L, -1));
                RecoverableError("OnInit failed in: " + script.scriptPath, Create::Exceptions::ComponentLoad).Handle();
                lua_pop(script.L, 1);
            }
        }
        else
        {
            lua_pop(script.L, 1);
        }
    }
    
}

void ScriptCore::CallAllUpdates(float dt)
{
    PROFILE_DEEP_SCOPE("ScriptCore::OnUpdate");


    for (auto& script : s_LuaStates)
    {
        PROFILE_SCOPE("Script: " + script.name);

        if (!script.L)
            continue;

        lua_getglobal(script.L, "OnUpdate");
        if (lua_isfunction(script.L, -1))
        {
            lua_pushnumber(script.L, dt);
            if (lua_pcall(script.L, 1, 0, 0) != LUA_OK)
            {
                Logger::LogError("[Lua] %s", lua_tostring(script.L, -1));
                RecoverableError("OnUpdate failed in: " + script.scriptPath, Create::Exceptions::ComponentLoad).Handle();
                lua_pop(script.L, 1);
            }
        }
        else
        {
            lua_pop(script.L, 1);
        }
    }
}

void ScriptCore::ReloadAllScripts()
{
    Logger::LogVerbose("[ScriptCore] Reloading all Lua scripts...");
    for (auto& script : s_LuaStates)
    {
        if (script.component)
        {
            script.component->ReloadScript();
        }
    }
}

void ScriptCore::PrintScriptDiagnostics()
{
    Logger::LogVerbose("[ScriptCore] Registered Scripts: %zu", s_LuaStates.size());
    for (const auto& script : s_LuaStates)
    {
        Logger::LogVerbose(" - Name: %s | Path: %s | Lua: %p | Component: %p",
                        script.name.c_str(),
                        script.scriptPath.c_str(),
                        (void*)script.L,
                        (void*)script.component);
    }
}
