#ifdef PLATFORM_LINUX

#include <UE4SSProgram.hpp>
#include <DynamicOutput/DynamicOutput.hpp>
#include <Helpers/String.hpp>

#include <dlfcn.h>
#include <link.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <filesystem>
#include <future>

namespace fs = std::filesystem;
using namespace RC;

static bool s_wait_for_ue4ss{};

auto get_module_path() -> std::string
{
    Dl_info info;
    if (dladdr((void*)get_module_path, &info) && info.dli_fname)
    {
        return info.dli_fname;
    }
    return "";
}

auto get_main_thread_id() -> pid_t
{
    // On Linux, the main thread is typically the one with the same PID as the process
    return getpid();
}

auto thread_library_start(UE4SSProgram* program) -> void*
{
    // Wrapper for entire program
    program->init();

    if (auto e = program->get_error_object(); e->has_error())
    {
        if (!Output::has_internal_error())
        {
            Output::send<LogLevel::Error>(STR("Fatal Error: {}\n"), ensure_str(e->get_message()));
        }
        else
        {
            fprintf(stderr, "Error: %s\n", e->get_message());
        }
    }

    return nullptr;
}

auto process_initialized() -> void
{
    std::string modulePath = get_module_path();
    if (modulePath.empty())
    {
        fprintf(stderr, "Failed to get module path\n");
        return;
    }

    auto program = new UE4SSProgram(modulePath, {});
    
    pthread_t thread;
    if (pthread_create(&thread, nullptr, (void*(*)(void*))thread_library_start, program) == 0)
    {
        pthread_detach(thread);
    }

    if (s_wait_for_ue4ss)
    {
        UE4SSProgram::cpp_mods_done_loading.wait(false, std::memory_order_relaxed);
    }
}

// Constructor attribute - this runs when the library is loaded via LD_PRELOAD
__attribute__((constructor))
auto library_loaded() -> void
{
    pid_t tid = syscall(SYS_gettid);
    pid_t main_tid = get_main_thread_id();
    
    if (tid == main_tid)
    {
        s_wait_for_ue4ss = true;
        // Defer initialization to avoid deadlocks during library loading
        std::thread(process_initialized).detach();
    }
    else
    {
        s_wait_for_ue4ss = false;
        process_initialized();
    }
}

// Destructor attribute - this runs when the library is unloaded
__attribute__((destructor))
auto library_unloaded() -> void
{
    UE4SSProgram::static_cleanup();
}

#endif // PLATFORM_LINUX
