// Logger.cpp -- Timestamped file logging and system diagnostics
#include "Logger.h"
#include "Utils.h"

FILE* g_logFile = NULL;
int   g_logging = 0;

void LogTimestamp() {
    SYSTEMTIME st;
    GetLocalTime(&st);
    if (g_logFile) fprintf(g_logFile, "[%04d-%02d-%02d %02d:%02d:%02d.%03d] ",
        st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
}

void Log(const char* msg) {
    if (!g_logFile) return;
    LogTimestamp();
    fprintf(g_logFile, "%s\n", msg);
    fflush(g_logFile);
}

void LogFmt(const char* fmt, ...) {
    if (!g_logFile) return;
    LogTimestamp();
    va_list args;
    va_start(args, fmt);
    vfprintf(g_logFile, fmt, args);
    va_end(args);
    fprintf(g_logFile, "\n");
    fflush(g_logFile);
}

void OpenLogFile() {
    if (g_logFile) return;
    char path[MAX_PATH];
    GetGameDir(path, MAX_PATH);
    strncat(path, "DDO_perf.log", MAX_PATH - strlen(path) - 1);
    g_logFile = fopen(path, "w");
}

void LogIf(const char* msg) {
    if (!g_logging) return;
    Log(msg);
}

void LogFmtIf(const char* fmt, ...) {
    if (!g_logging) return;
    if (!g_logFile) return;
    LogTimestamp();
    va_list args;
    va_start(args, fmt);
    vfprintf(g_logFile, fmt, args);
    va_end(args);
    fprintf(g_logFile, "\n");
    fflush(g_logFile);
}

void LogSystemInfo()
{
    LogIf("-------------------------------------------------");
    LogIf("System Information:");

    typedef NTSTATUS(WINAPI* RtlGetVersion_t)(OSVERSIONINFOEXW*);
    OSVERSIONINFOEXW osv = { sizeof(osv) };
    RtlGetVersion_t pRtlGetVersion = (RtlGetVersion_t)
        GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion");
    if (pRtlGetVersion && pRtlGetVersion(&osv) == 0)
        LogFmtIf("  Windows Version  : %lu.%lu.%lu (Build %lu)",
            osv.dwMajorVersion, osv.dwMinorVersion,
            osv.dwBuildNumber, osv.dwBuildNumber);
    else
        LogIf("  Windows Version  : Unknown");

    SYSTEM_INFO si = { 0 };
    GetSystemInfo(&si);
    LogFmtIf("  Processors       : %lu", si.dwNumberOfProcessors);

    MEMORYSTATUSEX ms = { sizeof(ms) };
    if (GlobalMemoryStatusEx(&ms))
        LogFmtIf("  Physical RAM     : %llu MB total, %llu MB free",
            ms.ullTotalPhys / (1024 * 1024),
            ms.ullAvailPhys / (1024 * 1024));

    LogFmtIf("  Process ID       : %lu", GetCurrentProcessId());

    BOOL isWow64 = FALSE;
    typedef BOOL(WINAPI* LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
    LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS)
        GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process");
    if (fnIsWow64Process) fnIsWow64Process(GetCurrentProcess(), &isWow64);
    LogFmtIf("  Is 64-bit OS     : %s", isWow64 ? "Yes" : "No");

    char exePath[MAX_PATH] = { 0 };
    GetModuleFileNameA(NULL, exePath, MAX_PATH);
    LogFmtIf("  EXE Path         : %s", exePath);
}

void LogLoadedModules()
{
    LogIf("-------------------------------------------------");
    LogIf("Loaded Modules:");

    HANDLE hSnap = CreateToolhelp32Snapshot(
        TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, GetCurrentProcessId());
    if (hSnap == INVALID_HANDLE_VALUE)
    {
        LogFmtIf("  ERROR: CreateToolhelp32Snapshot failed (GLE=%lu)", GetLastError());
        return;
    }

    MODULEENTRY32 me = { sizeof(me) };
    if (Module32First(hSnap, &me))
    {
        do {
            LogFmtIf("  0x%08X - 0x%08X  %s",
                (DWORD)me.modBaseAddr,
                (DWORD)me.modBaseAddr + me.modBaseSize,
                me.szExePath);
        } while (Module32Next(hSnap, &me));
    }
    else LogFmtIf("  ERROR: Module32First failed (GLE=%lu)", GetLastError());

    CloseHandle(hSnap);
}

void LogFunctionBytes(const char* name, void* fn)
{
    if (!g_logging || !g_logFile || !fn) return;
    BYTE* p = (BYTE*)fn;
    const char* hookNote = "";
    if (p[0] == 0xE9)                      hookNote = "  <-- ALREADY HOOKED (JMP)";
    else if (p[0] == 0xFF && p[1] == 0x25) hookNote = "  <-- ALREADY HOOKED (JMP indirect)";
    LogFmtIf("  %-36s @ 0x%08X  bytes: %02X %02X %02X %02X %02X %02X %02X %02X%s",
        name, (DWORD)fn,
        p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
        hookNote);
}
