#include "Log.h"
#include "../Console.h"
#include "raylib.h"
#include <ctime>
#include <cwchar>

Log* Log::Get()
{
    static Log instance;
    return &instance;
}

Log::Log()
{
    m_hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    InitializeCriticalSection(&m_cs);

    // Create Log directory next to exe
    wchar_t exePath[MAX_PATH];
    GetModuleFileNameW(NULL, exePath, MAX_PATH);
    wchar_t* p = wcsrchr(exePath, L'\\');
    if (p) *p = L'\0';
    wchar_t logDir[MAX_PATH];
    swprintf_s(logDir, L"%s\\Logs", exePath);
    CreateDirectoryW(logDir, NULL);

    // Open dated log file: ./Logs/2026-06-04.log
    SYSTEMTIME st;
    GetLocalTime(&st);
    wchar_t logPath[MAX_PATH];
    swprintf_s(logPath, L"%s\\Rcjy %04u-%02u-%02u.log",
        logDir, st.wYear, st.wMonth, st.wDay);
    _wfopen_s(&m_fp, logPath, L"a");
}

Log::~Log()
{
    if (m_fp) fclose(m_fp);
    DeleteCriticalSection(&m_cs);
}

void Log::Info(const wchar_t* fmt, ...)    { va_list a; va_start(a, fmt); Write(LogLevel::Info,    fmt, a); va_end(a); }
void Log::Warning(const wchar_t* fmt, ...) { va_list a; va_start(a, fmt); Write(LogLevel::Warning, fmt, a); va_end(a); }
void Log::Error(const wchar_t* fmt, ...)   { va_list a; va_start(a, fmt); Write(LogLevel::Error,   fmt, a); va_end(a); }

void Log::Write(LogLevel level, const wchar_t* fmt, va_list args)
{
    wchar_t msg[1024];
    _vsnwprintf_s(msg, _countof(msg), _TRUNCATE, fmt, args);
    WriteRaw(level, msg);
}

void Log::WriteRaw(LogLevel level, const wchar_t* msg)
{
    EnterCriticalSection(&m_cs);

    // Console: message only, with color
    SetColor(level);
    Console::NotifyOutputPending();
    WriteConsoleW(m_hOut, msg, (DWORD)wcslen(msg), NULL, NULL);
    WriteConsoleW(m_hOut, L"\n", 1, NULL, NULL);
    Console::NotifyOutputDone();
    ResetColor();

    // File: timestamp + message
    if (m_fp) {
        SYSTEMTIME st;
        GetLocalTime(&st);
        fwprintf(m_fp, L"[%02u:%02u:%02u] %s\n", st.wHour, st.wMinute, st.wSecond, msg);
        fflush(m_fp);
    }

    LeaveCriticalSection(&m_cs);
}

void Log::SetColor(LogLevel level)
{
    if (!m_hOut) return;
    WORD attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
    switch (level)
    {
    case LogLevel::Info:    break;                                                // white
    case LogLevel::Warning: attr = FOREGROUND_RED | FOREGROUND_GREEN;             break; // yellow
    case LogLevel::Error:   attr = FOREGROUND_RED | FOREGROUND_INTENSITY;         break; // bright red
    }
    SetConsoleTextAttribute(m_hOut, attr);
}

void Log::ResetColor()
{
    if (m_hOut)
        SetConsoleTextAttribute(m_hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
}

void Log::RaylibTraceCallback(int logLevel, const char* text, va_list args)
{
    // Format the ANSI message
    char ansiMsg[1024];
    vsnprintf(ansiMsg, sizeof(ansiMsg), text, args);

    // Convert to wide string
    wchar_t wideMsg[1024];
    MultiByteToWideChar(CP_UTF8, 0, ansiMsg, -1, wideMsg, _countof(wideMsg));

    // Map raylib log level to our LogLevel
    LogLevel lvl;
    switch (logLevel) {
        case 0: /* LOG_INFO */    lvl = LogLevel::Info;    break;
        case 1: /* LOG_WARNING */ lvl = LogLevel::Warning; break;
        case 2: /* LOG_ERROR */
        case 3: /* LOG_FATAL */   lvl = LogLevel::Error;   break;
        default:                  return; // LOG_DEBUG / LOG_NONE — skip
    }

    Log::Get()->WriteRaw(lvl, wideMsg);
}

void Log::RedirectRaylibTrace()
{
    RLSetTraceLogCallback(Log::RaylibTraceCallback);
}
