#include "pch.h" 
#include "CoVinceWindowsOverlay.h"
//extra test log file
#include <ShlObj.h> // For SHGetKnownFolderPath
//extra test log file
#pragma comment(lib, "Shell32.lib") // Link against Shell32.lib (often needed for SH functions)

using namespace Gdiplus; // Optional: use namespace

#pragma comment (lib,"Gdiplus.lib") // Link against Gdiplus library

// --- Forward Declarations ---
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);



// --- Global variables ---
std::vector<HWND> g_overlayWindows; // Use g_ prefix for global convention
std::wstring g_overlayText = L"";
std::atomic<bool> g_showOverlayText = false; // Use atomic for potential thread safety
int g_borderWidth = 0;
COLORREF g_borderColor = RGB(0, 0, 0);
int g_fontSize = 20;
std::atomic<CoVinceOverlayTextAlignment> g_textAlignment = COVINCE_OVERLAY_TEXT_ALIGN_VCENTER;
std::atomic<bool> g_overlayShown = false; // Use atomic for thread safety

const wchar_t* WND_CLASS_NAME = L"CoVinceOverlayWindowClass";
const int POINTER_NAME_FONT_SIZE = 12; // Example font size for names


// --- Add GDI+ Globals ---
ULONG_PTR g_gdiplusToken;
GdiplusStartupInput g_gdiplusStartupInput;

// --- Remote Pointer Definitions & State ---
struct RemotePointer 
{
    std::wstring userName;
    POINT position = { 0, 0 };  // Absolute screen coordinates (x, y)
    std::wstring imagePath;     // Path to the image file
    int displayWidth = 0;       // Desired display width
    int displayHeight = 0;      // Desired display height
    // No need for cached Bitmap pointer here, we use the central cache
};

std::map<std::wstring, RemotePointer> g_remotePointers; // Key: userId (wstring)
CRITICAL_SECTION g_pointersCritSec;

// --- Image Cache ---
std::map<std::wstring, Gdiplus::Bitmap*> g_imageCache; // Key: imagePath (wstring)
CRITICAL_SECTION g_imageCacheCritSec;
const size_t MAX_CACHE_ITEMS = 100;

// --- Logging Globals ---
std::ofstream g_logFileStream;
CRITICAL_SECTION g_logCritSec;
bool g_logInitialized = false;
wchar_t g_logFilePath[MAX_PATH] = { 0 }; // Store the determined log file path

// Bool for switching log to file or log to debug
std::atomic<bool> g_logToDebugView = false; // True uses DebugView logging and False Default to file logging

// Extra safety net to make sure DLL is initialized correctly before starting any functions
static std::atomic<bool> g_dllInitializedOk = false;



// --- Logging Implementation ---

// Helper to initialize logging (opens file, etc.) - Not thread-safe itself, call within lock

bool InitializeLoggingInternal()
{
    OutputDebugStringA("InitializeLoggingInternal: START \n");
    if (g_logInitialized) return true; // Already done

    // 1. Get Temp Path
    DWORD pathLen = GetTempPathW(MAX_PATH, g_logFilePath);
    if (pathLen == 0 || pathLen >= MAX_PATH) {
        OutputDebugStringA("InitializeLoggingInternal: OverlayLog Error: Failed to get temp path.\n");
        return false;
    }


    // 2. Generate Unique Filename Part (Timestamp + PID)
    // Get current time
    auto now = std::chrono::system_clock::now();
    auto now_c = std::chrono::system_clock::to_time_t(now);
    std::tm now_tm;
    localtime_s(&now_tm, &now_c); // Use localtime_s for safety

    // Format timestamp (YYYYMMDD_HHMMSS) into a wide string buffer
    wchar_t timestampStr[80];
    wcsftime(timestampStr, sizeof(timestampStr) / sizeof(wchar_t), L"%Y%m%d_%H%M%S", &now_tm);

    // Get Process ID
    DWORD processId = GetCurrentProcessId();

    // Create the unique filename
    wchar_t uniqueFilename[MAX_PATH];
    swprintf_s(uniqueFilename, MAX_PATH, L"\\CoVinceOverlayLog_%s_%lu.txt", timestampStr, processId);

    // 3. Append Unique Filename to Temp Path
    // Ensure we don't exceed MAX_PATH when concatenating
    if (wcslen(g_logFilePath) + wcslen(uniqueFilename) < MAX_PATH) {
        wcscat_s(g_logFilePath, MAX_PATH, uniqueFilename);
    }
    else {
        OutputDebugStringA("OverlayLog Error: Calculated log file path exceeds MAX_PATH.\n");
        // Fallback: Use a simpler name directly in temp? Or just fail?
        wcscpy_s(g_logFilePath, MAX_PATH, L"CoVinceOverlayLog_ErrorPath.txt"); // Fallback name
    }


    // 4. Open file stream in WRITE mode (std::ios::out) - truncates if exists
    g_logFileStream.open(g_logFilePath, std::ios::out); // REMOVED std::ios::app

    if (!g_logFileStream.is_open()) {
        OutputDebugStringA("OverlayLog Error: Failed to open log file.\n");
        wchar_t errorMsg[512];
        swprintf_s(errorMsg, L"OverlayLog Error: Failed to open %ls\n", g_logFilePath);
        OutputDebugStringW(errorMsg);
        return false;
    }

    g_logInitialized = true;

    // --- Log header information ---
    g_logFileStream << "----------------------------------------" << std::endl;
    g_logFileStream << "Logging Initialized: " << std::put_time(&now_tm, "%Y-%m-%d %H:%M:%S") << std::endl;

    // Convert wide path to multibyte for logging to the narrow stream
    char multiBytePath[MAX_PATH * 2];
    int bytesWritten = WideCharToMultiByte(CP_UTF8, 0, g_logFilePath, -1, multiBytePath, sizeof(multiBytePath), NULL, NULL);
    if (bytesWritten > 0) {
        g_logFileStream << "Log File Path: " << multiBytePath << std::endl;
    }
    else {
        g_logFileStream << "Log File Path: [Error converting path to UTF-8]" << std::endl;
    }
    g_logFileStream << "Process ID: " << processId << std::endl;
    g_logFileStream << "----------------------------------------" << std::endl;
    g_logFileStream.flush(); // Ensure header is written

    OutputDebugStringA("OverlayLog Initialized.\n");
    OutputDebugStringW(g_logFilePath); // Show the exact path in DebugView
    OutputDebugStringA("\n");

    return true;
}




// Main logging function (Thread-Safe)
void LogToFile(const char* format, ...) // Or rename LogMessage
{
    OutputDebugStringA("LogToFile: START \n");

    // --- Common Timestamp and Message Formatting ---
    std::string timestampStr = "[TimeERR] "; // Default timestamp in case of error
    std::string messageStr = "Error formatting log message."; // Default message

    EnterCriticalSection(&g_logCritSec);
    try // Outer try for exception safety during lock
    {
        // Try to format timestamp
        try 
        {
            OutputDebugStringA("LogToFile: Try to format timestamp. \n");
            auto now = std::chrono::system_clock::now();
            auto now_c = std::chrono::system_clock::to_time_t(now);
            auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
            std::tm now_tm;
            if (localtime_s(&now_tm, &now_c) == 0) 
            { // Check return value of localtime_s
                std::ostringstream timeSs;
                timeSs << std::put_time(&now_tm, "[%H:%M:%S.") << std::setfill('0') << std::setw(3) << ms.count() << "] ";
                timestampStr = timeSs.str(); // Assign if successful
            }
        }
        catch (const std::exception& e) 
        { 
            OutputDebugStringA("!!! EXCEPTION during DebugView logging: ");
            OutputDebugStringA(e.what());
            OutputDebugStringA("\n");
        }
        // Try to format user message
        try 
        {
            OutputDebugStringA("LogToFile: Try to format user message. \n");
            char messageBuffer[2048]; // Keep buffer local
            va_list args;
            va_start(args, format);
            int result = vsnprintf_s(messageBuffer, sizeof(messageBuffer), _TRUNCATE, format, args);
            va_end(args);
            if (result >= 0) 
            { // Check formatting success
                messageStr = messageBuffer; // Assign if successful
            }
        }
        catch (...) 
        { 
            OutputDebugStringA("LogToFile: potential va_list or vsnprintf_s issues. \n");
        }
        // --- Conditional Output ---
        if (g_logToDebugView.load()) // Check the flag
        {
            // --- Output to DebugView ---
            OutputDebugStringA(timestampStr.c_str());
            OutputDebugStringA(messageStr.c_str());
            OutputDebugStringA("\n");
        }
        else // --- Output to File ---
        {
            if (!g_logInitialized) 
            {
                if (!InitializeLoggingInternal()) 
                {
                    LeaveCriticalSection(&g_logCritSec); // Need to LEAVE CS before returning!
                    return; // Init failed
                }
            }
            if (!g_logFileStream.is_open()) 
            {
                OutputDebugStringA("LogToFile: Log failure? OutputDebugStringA? Cannot write to file. \n");
                LeaveCriticalSection(&g_logCritSec); // Need to LEAVE CS before returning!
                return; // File not open
            }
            // Write the pre-formatted strings to file
            g_logFileStream << timestampStr << messageStr << std::endl;
        }
    }
    catch (const std::exception& e) // Catch known C++ exceptions
    { 
        OutputDebugStringA("!!! LogToFile: EXCEPTION caught in outer try: ");
        OutputDebugStringA(e.what());
        OutputDebugStringA("\n");
        LeaveCriticalSection(&g_logCritSec); // Ensure unlock before returning/rethrowing
        return; // Exit after logging exception
    }
    catch (...) // Catch ANY other exception (e.g., structured exceptions)
    { 
        OutputDebugStringA("!!! LogToFile: UNKNOWN EXCEPTION caught in outer try block!\n");
        LeaveCriticalSection(&g_logCritSec); // Ensure unlock before returning/rethrowing
        return; // Exit after logging exception
    }
    LeaveCriticalSection(&g_logCritSec); // Unlock on normal exit
    // Mutex automatically unlocked when lock_guard goes out of scope
}

// --- Make sure helper assumes caller holds lock, or make helper use lock ---
// Modified version that uses the lock INTERNALLY for safety when called from DllMain DETACH
void ShutdownLoggingInternal()
{
    // If file logging was active (g_logInitialized is true)
    if (!g_logToDebugView.load() && g_logInitialized) // Check if file logging was intended and initialized
    {
        EnterCriticalSection(&g_logCritSec); // Lock here for safety during file close
        try {
            // Re-check inside lock to be absolutely sure state hasn't changed
            if (g_logInitialized && g_logFileStream.is_open()) {
                auto now = std::chrono::system_clock::now();
                auto now_c = std::chrono::system_clock::to_time_t(now);
                std::tm now_tm;
                localtime_s(&now_tm, &now_c);

                g_logFileStream << "----------------------------------------" << std::endl;
                g_logFileStream << "Logging Stopped: " << std::put_time(&now_tm, "%Y-%m-%d %H:%M:%S") << std::endl;
                g_logFileStream << "----------------------------------------" << std::endl;
                g_logFileStream.flush();
                g_logFileStream.close();
                OutputDebugStringA("OverlayLog File Closed.\n");
            }
            g_logInitialized = false; // Reset flag inside lock after closing/checking
        }
        catch (const std::exception& e) {
            OutputDebugStringA("!!! EXCEPTION in ShutdownLoggingInternal closing file: ");
            OutputDebugStringA(e.what());
            OutputDebugStringA("\n");
            // Ensure leave happens even on exception
        }
        catch (...) {
            OutputDebugStringA("!!! UNKNOWN EXCEPTION in ShutdownLoggingInternal closing file!!!\n");
        }
        LeaveCriticalSection(&g_logCritSec); // Leave critical section
    }
    else {
        // File logging wasn't active or needed closing
        OutputDebugStringA("OverlayLog Shutdown (File logging was not active/open).\n");
        g_logInitialized = false; // Ensure flag is false anyway
    }
}



// --- Helper Functions ---

// Registers the window class if it doesn't exist
HRESULT RegisterOverlayWindowClass(HINSTANCE hInstance)
{
    //LogToFile("RegisterOverlayWindowClass START");
    WNDCLASSEXW wcCheck = { sizeof(WNDCLASSEXW) };
    if (GetClassInfoExW(hInstance, WND_CLASS_NAME, &wcCheck))
    {
        //LogToFile("Window class '%ls' already registered.", WND_CLASS_NAME);
        return S_OK; // Already registered
    }

    //LogToFile("Registering window class '%ls'.", WND_CLASS_NAME);
    WNDCLASSEXW wc = { 0 };
    wc.cbSize = sizeof(WNDCLASSEXW);
    wc.style = CS_HREDRAW | CS_VREDRAW; // Redraw on size change
    wc.lpfnWndProc = WindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = NULL;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL; // Transparent background handled by layering
    wc.lpszMenuName = NULL;
    wc.lpszClassName = WND_CLASS_NAME;
    wc.hIconSm = NULL;

    if (!RegisterClassExW(&wc))
    {
        DWORD error = GetLastError();
        //LogToFile("RegisterClassExW failed for '%ls'. Error: %lu", WND_CLASS_NAME, error);
        return HRESULT_FROM_WIN32(error);
    }
    return S_OK;
}

// --- Cleanup Helper for Image Cache ---
void ClearImageCacheInternal()
{
    size_t count = g_imageCache.size(); // Get count before clear
    if (count == 0) return; // Nothing to clear

    //LogToFile("ClearImageCacheInternal: Clearing %zu items...", count);
    // Use iterator loop for safety and C++14 compatibility if needed
    for (auto it = g_imageCache.begin(); it != g_imageCache.end(); ++it) {
        delete it->second; // Delete the GDI+ Bitmap object
    }
    // Alternative C++17 loop (requires /std:c++17 or later)
    // for (auto const& [path, bitmap] : g_imageCache) {
    //     delete bitmap;
    // }
    g_imageCache.clear();
    //LogToFile("Image cache cleared (%zu items).", count);
}

// --- Window Procedure Including mouse pointer receive ---
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    // LogToFile("WindowProc START"); // Keep this commented unless essential; it's very frequent.
    switch (uMsg)
    {
    case WM_PAINT:
    {
        // LogToFile("WM_PAINT START"); // Also potentially very frequent
        // --- Setup Code (GetClientRect, width, height checks, Create Memory DC/Bitmap) ---
        RECT rcClient;
        if (!GetClientRect(hwnd, &rcClient)) return 0;
        int width = rcClient.right - rcClient.left;
        int height = rcClient.bottom - rcClient.top;
        if (width <= 0 || height <= 0) { PAINTSTRUCT ps; BeginPaint(hwnd, &ps); EndPaint(hwnd, &ps); return 0; }

        HDC hdcScreen = GetDC(hwnd); if (!hdcScreen) return 0;
        HDC memHDC = CreateCompatibleDC(hdcScreen); if (!memHDC) { ReleaseDC(hwnd, hdcScreen); return 0; }


        BITMAPINFO bmi; ZeroMemory(&bmi, sizeof(BITMAPINFO));
        bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        bmi.bmiHeader.biWidth = width; bmi.bmiHeader.biHeight = -height; // Top-down DIB
        bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32;
        bmi.bmiHeader.biCompression = BI_RGB;
        HBITMAP memBitmap = CreateDIBSection(memHDC, &bmi, DIB_RGB_COLORS, nullptr, NULL, 0);

        if (!memBitmap) { DeleteDC(memHDC); ReleaseDC(hwnd, hdcScreen); return 0; }
        HBITMAP hOldBitmap = (HBITMAP)SelectObject(memHDC, memBitmap);
        // ------------------------------------------------------------------------------


        // --- GDI+ Drawing Starts ---
        { // Scope for Graphics object
            //LogToFile("GDI+ Drawing START");
            Graphics graphics(memHDC);
            graphics.SetSmoothingMode(SmoothingModeAntiAlias);
            graphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
            graphics.Clear(Color(0, 0, 0, 0)); // Clear transparent

            // --- Draw Border (No locking needed for reading globals here) ---
            if (g_borderWidth > 0) {
                Color borderGdiplusColor(255, GetRValue(g_borderColor), GetGValue(g_borderColor), GetBValue(g_borderColor));
                Pen borderPen(borderGdiplusColor, (REAL)g_borderWidth);
                REAL penOffset = (REAL)g_borderWidth / 2.0f;
                graphics.DrawRectangle(&borderPen, penOffset, penOffset, (REAL)width - g_borderWidth, (REAL)height - g_borderWidth);
            }


            // --- Draw Overlay Text ---
            if (g_showOverlayText.load())
            {
                //LogToFile("Draw Text START");
                FontFamily fontFamily(L"Arial");
                Font font(&fontFamily, (REAL)g_fontSize, FontStyleRegular, UnitPixel);
                SolidBrush textBrush(Color(255, 255, 255, 255));
                
                // --- GDI+ Alignment Logic ---
                StringFormat format;
                CoVinceOverlayTextAlignment currentAlignment = g_textAlignment.load(); // Use .load() for atomics

                // Horizontal Alignment (GDI+ StringAlignment enum)
                if ((currentAlignment & COVINCE_OVERLAY_TEXT_ALIGN_CENTER) != 0) {
                    format.SetAlignment(StringAlignmentCenter); // GDI+ Center
                }
                else if ((currentAlignment & COVINCE_OVERLAY_TEXT_ALIGN_RIGHT) != 0) {
                    format.SetAlignment(StringAlignmentFar);    // GDI+ Right (Far)
                }
                else { // Default Left
                    format.SetAlignment(StringAlignmentNear);   // GDI+ Left (Near)
                }

                // Vertical Alignment (GDI+ StringAlignment enum)
                if ((currentAlignment & COVINCE_OVERLAY_TEXT_ALIGN_VCENTER) != 0) {
                    format.SetLineAlignment(StringAlignmentCenter); // GDI+ Middle (Center)
                }
                else if ((currentAlignment & COVINCE_OVERLAY_TEXT_ALIGN_BOTTOM) != 0) {
                    format.SetLineAlignment(StringAlignmentFar);    // GDI+ Bottom (Far)
                }
                else { // Default Top
                    format.SetLineAlignment(StringAlignmentNear);   // GDI+ Top (Near)
                }
                // --- End GDI+ Alignment Logic ---
                
                // Calculate Layout Rectangle (with padding)
                RectF layoutRectF(
                    (REAL)rcClient.left + g_borderWidth + 2,
                    (REAL)rcClient.top + g_borderWidth + 2,
                    (REAL)(rcClient.right - rcClient.left) - 2.0f * (g_borderWidth + 2),
                    (REAL)(rcClient.bottom - rcClient.top) - 2.0f * (g_borderWidth + 2)
                );

                // Ensure width and height are not negative if padding is too large
                if (layoutRectF.Width < 0) layoutRectF.Width = 0;
                if (layoutRectF.Height < 0) layoutRectF.Height = 0;

                // Now use layoutRectF in DrawString
                graphics.DrawString(g_overlayText.c_str(), -1, &font, layoutRectF, &format, &textBrush);
            }

            // --- REVISED: Remote Pointer Drawing (using CRITICAL_SECTION) ---
            { // Scope for lock and GDI+ resources for pointers
                //LogToFile("Remote Pointer Drawing START");
                std::vector<RemotePointer> pointersToDraw; // Copy data to draw outside lock

                // --- Lock Pointer Map ---
                EnterCriticalSection(&g_pointersCritSec); // *** USE CRITICAL_SECTION ***
                try {
                    if (!g_remotePointers.empty()) {
                        pointersToDraw.reserve(g_remotePointers.size());
                        for (const auto& pair : g_remotePointers) {
                            pointersToDraw.push_back(pair.second);
                        }
                    }
                }
                catch (...) {
                    LeaveCriticalSection(&g_pointersCritSec); // *** LEAVE CS on exception ***
                    //LogToFile("WM_PAINT: Exception copying pointer data!");
                    goto PaintCleanup; // Skip drawing pointers if copy failed
                }
                LeaveCriticalSection(&g_pointersCritSec); // *** LEAVE CS on normal path ***
                // --- Unlocked Pointer Map ---


                if (!pointersToDraw.empty())
                {
                    RECT windowRect;
                    BOOL gotWindowRect = GetWindowRect(hwnd, &windowRect);

                    if (gotWindowRect)
                    {
                        // Create GDI+ resources needed for the loop ONCE
                        FontFamily nameFontFamily(L"Arial"); // Potential crash point?
                        Font nameFont(&nameFontFamily, (REAL)POINTER_NAME_FONT_SIZE, FontStyleRegular, UnitPixel);
                        SolidBrush nameBrush(Color(255, 255, 255, 255));
                        StringFormat nameFormat;

                        for (const auto& pointer : pointersToDraw)
                        {
                            int relativeX = pointer.position.x - windowRect.left;
                            int relativeY = pointer.position.y - windowRect.top;
                            int displayWidth = pointer.displayWidth;
                            int displayHeight = pointer.displayHeight;

                            // Basic bounds check (check if top-left corner is within reasonable bounds)
                            if (relativeX < width && relativeX + displayWidth > 0 &&
                                relativeY < height && relativeY + displayHeight > 0)
                            {
                                Gdiplus::Bitmap* cachedBitmap = nullptr;

                                // --- Lock Image Cache for Lookup ---
                                EnterCriticalSection(&g_imageCacheCritSec); // *** USE CRITICAL_SECTION ***
                                try {
                                    auto it = g_imageCache.find(pointer.imagePath);
                                    if (it != g_imageCache.end()) {
                                        cachedBitmap = it->second;
                                    }
                                }
                                catch (...) {
                                    LeaveCriticalSection(&g_imageCacheCritSec); // *** LEAVE CS on exception ***
                                    //LogToFile("WM_PAINT: Exception looking up image cache for '%ls'!", pointer.imagePath.c_str());
                                    continue; // Skip this pointer
                                }
                                LeaveCriticalSection(&g_imageCacheCritSec); // *** LEAVE CS on normal path ***
                                // --- Unlocked Image Cache ---

                                if (cachedBitmap)
                                {
                                    // --- Draw Rounded Image ---
                                    GraphicsPath path;
                                    path.AddEllipse(relativeX, relativeY, displayWidth, displayHeight);
                                    GraphicsState state = graphics.Save(); // Save state is good practice with clipping
                                    graphics.SetClip(&path, CombineModeReplace);
                                    graphics.DrawImage(cachedBitmap, relativeX, relativeY, displayWidth, displayHeight);
                                    graphics.Restore(state); // Restore state (resets clip)
                                }
                                else
                                {
                                    //LogToFile("WM_PAINT: Image not in cache for path: %ls", pointer.imagePath.c_str());
                                    // Optional: Draw fallback dot here if needed
                                }

                                // --- Draw Name ---
                                if (!pointer.userName.empty())
                                {
                                    PointF textOrigin((REAL)relativeX + displayWidth + 3,
                                        (REAL)relativeY + (REAL)displayHeight / 2.0f - (REAL)POINTER_NAME_FONT_SIZE / 2.0f);
                                    graphics.DrawString(pointer.userName.c_str(), -1, &nameFont, textOrigin, &nameFormat, &nameBrush);
                                }
                            } // End bounds check
                        } // End for loop
                    } // End if gotWindowRect
                } // End if pointersToDraw not empty
            } // End scope for pointer drawing

        PaintCleanup: // Label for cleanup, especially after exceptions before UpdateLayeredWindow
            // --- Update Layered Window ---
            POINT ptSrc = { 0, 0 };
            POINT ptDst = { rcClient.left, rcClient.top };
            RECT windowRectULW; // Use different variable name
            if (GetWindowRect(hwnd, &windowRectULW)) { ptDst.x = windowRectULW.left; ptDst.y = windowRectULW.top; }
            SIZE sizeWnd = { width, height };
            BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
            BOOL success = UpdateLayeredWindow(hwnd, hdcScreen, &ptDst, &sizeWnd, memHDC, &ptSrc, 0, &blend, ULW_ALPHA);
            // if (!success) { LogToFile("UpdateLayeredWindow failed: %lu", GetLastError()); }

            // --- Cleanup ---
            SelectObject(memHDC, hOldBitmap);
            DeleteObject(memBitmap);
            DeleteDC(memHDC);
            ReleaseDC(hwnd, hdcScreen);

            ValidateRect(hwnd, NULL);
            return 0;
        } // End WM_PAINT
    }

    case WM_DESTROY:
        //LogToFile("WM_DESTROY received for HWND %p", hwnd);
        return 0;

    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
    //LogToFile("MonitorEnumProc START");
    MONITORINFO mi = { sizeof(MONITORINFO) };
    if (!GetMonitorInfo(hMonitor, &mi))
    {
        //LogToFile("MonitorEnumProc: GetMonitorInfo failed for HMONITOR %p. Error: %lu", hMonitor, GetLastError());
        return TRUE; // Continue enumeration
    }

    HINSTANCE hInstance = GetModuleHandle(NULL); // Get current module handle
    if (!hInstance) 
    {
        //LogToFile("MonitorEnumProc: GetModuleHandle(NULL) failed. Error: %lu", GetLastError());
        return TRUE; // Cannot create window without instance
    }

    // Create the overlay window for this monitor
    //LogToFile("MonitorEnumProc: Create the overlay window START");
    HWND hwnd = CreateWindowExW(
        WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOACTIVATE, // Added NOACTIVATE
        WND_CLASS_NAME,
        L"Overlay", // Window title (not visible)
        WS_POPUP,   // Style for borderless window
        mi.rcMonitor.left,
        mi.rcMonitor.top,
        mi.rcMonitor.right - mi.rcMonitor.left,
        mi.rcMonitor.bottom - mi.rcMonitor.top,
        NULL, NULL, hInstance, NULL
    );

    if (!hwnd)
    {
        //LogToFile("MonitorEnumProc: CreateWindowExW failed for monitor %p. Error: %lu", hMonitor, GetLastError());
        return TRUE; // Continue enumeration even if one window fails
    }

    // Show the window without activating it
    ShowWindow(hwnd, SW_SHOWNA);
    //UpdateWindow(hwnd); // Force initial paint
    InvalidateRect(hwnd, NULL, TRUE); // Request a repaint immediately

    // Safely add HWND to the vector (passed via dwData)
    auto* pWindowsVec = reinterpret_cast<std::vector<HWND>*>(dwData);
    if (pWindowsVec) 
    {
        try 
        {
            pWindowsVec->push_back(hwnd);
        }
        catch (const std::bad_alloc&) 
        {
            //LogToFile("MonitorEnumProc: Failed to allocate memory to store HWND %p.", hwnd);
            DestroyWindow(hwnd); // Clean up
            // Decide whether to stop or continue enumeration on memory error
            return FALSE; // Stop enumeration on critical error
        }
    }
    return TRUE; // Continue enumeration
}


#ifdef __cplusplus
extern "C" {
#endif
    // --- Overlay Functions Implementation ---
    COVINCEWINDOWSOVERLAY_API HRESULT __cdecl ShowOverlay(const wchar_t* text, bool showText, int borderW, int r, int g, int b, int fSize, CoVinceOverlayTextAlignment alignment)
    {
        // --- ADD INITIALIZATION CHECK AT THE VERY START ---
        if (!g_dllInitializedOk.load(std::memory_order_acquire)) // Use acquire load for visibility
        {
            OutputDebugStringA("ShowOverlay called but DLL not initialized!\n"); // Safer log here
            return COVINCE_E_NOT_INITIALIZED;
        }
        // --- END INITIALIZATION CHECK ---
        LogToFile("ShowOverlay START");
        LogToFile("ShowOverlay called. Border width: %d, Font Size: %d", borderW, fSize);
        HRESULT hr = S_OK;

        // --- Cleanup previous instance ---
        hr = HideOverlay(); // Call HideOverlay first to ensure clean state
        if (FAILED(hr))
        {
            LogToFile("ShowOverlay: HideOverlay failed with HRESULT: 0x%X", hr);
            // Decide if this is fatal. Maybe continue? For now, return error.
            return hr;
        }
        LogToFile("ShowOverlay: Previous overlay hidden successfully.");

        // --- Input Validation ---
        if (!text) 
        {
            LogToFile("ShowOverlay error: text pointer is NULL.");
            return E_POINTER; // Standard HRESULT for null pointer
        }

        if (borderW < 0) borderW = 0; // Basic validation
        if (fSize <= 0) fSize = 1;    // Basic validation

        // --- Update Global State ---
        try 
        {
            g_overlayText = text; // Assign C-style string to std::wstring
        }
        catch (const std::exception& e) 
        {
            LogToFile("ShowOverlay: EXCEPTION assigning text: %s", e.what());
            return E_FAIL; // Indicate failure
        }
        catch (...) 
        {
            LogToFile("ShowOverlay: UNKNOWN EXCEPTION assigning text!");
            return E_FAIL;
        }
        // --- End Text Assignment Change ---

        g_showOverlayText = showText;
        g_borderWidth = borderW;
        g_borderColor = RGB(r, g, b);
        g_fontSize = fSize;
        g_textAlignment = alignment;

        // --- Register Window Class ---
        HINSTANCE hInstance = GetModuleHandle(NULL);
        if (!hInstance) 
        {
            DWORD error = GetLastError();
            LogToFile("ShowOverlay: GetModuleHandle(NULL) failed. Error: %lu", error);
            return HRESULT_FROM_WIN32(error);
        }
        hr = RegisterOverlayWindowClass(hInstance);
        if (FAILED(hr)) 
        {
            LogToFile("ShowOverlay: RegisterOverlayWindowClass failed with HRESULT: 0x%X", hr);
            return COVINCE_E_CLASS_REGISTRATION_FAILED; // Or return hr directly
        }

        // --- Create Windows on All Monitors ---
        std::vector<HWND> tempWindows; // Create windows into a temporary vector
        LPARAM lParam = reinterpret_cast<LPARAM>(&tempWindows);
        if (!EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, lParam))
        {
            // If MonitorEnumProc returned FALSE, it might be due to allocation failure
            // Otherwise, EnumDisplayMonitors itself failed.
            DWORD error = GetLastError();
            LogToFile("ShowOverlay: EnumDisplayMonitors failed. Error: %lu", error);
            // Clean up any windows that might have been created in tempWindows before failure
            for (HWND hwnd : tempWindows) 
            {
                if (IsWindow(hwnd)) DestroyWindow(hwnd);
            }
            return HRESULT_FROM_WIN32(error);
        }

        // --- Finalize State ---
        if (tempWindows.empty()) 
        {
            LogToFile("ShowOverlay: No monitors found or window creation failed on all monitors.");
            // Technically not an error based on EnumDisplayMonitors, but maybe return a specific code?
            // Or just S_OK as no API failed? Let's return OK but log it.
            return S_OK;
        }

        g_overlayWindows = std::move(tempWindows); // Move ownership to global vector
        g_overlayShown = true;
        LogToFile("ShowOverlay created %zu windows. Returning S_OK.", g_overlayWindows.size());
        return S_OK;
    }
    
    COVINCEWINDOWSOVERLAY_API HRESULT __cdecl ShowOverlayOnMonitor(int monitorIndex, const wchar_t* text, bool showText, int borderW, int r, int g, int b, int fSize, CoVinceOverlayTextAlignment alignment)
    {
        // --- ADD INITIALIZATION CHECK AT THE VERY START ---
        if (!g_dllInitializedOk.load(std::memory_order_acquire)) // Use acquire load for visibility
        {
            OutputDebugStringA("ShowOverlayOnMonitor called but DLL not initialized!\n"); // Safer log here
            return COVINCE_E_NOT_INITIALIZED;
        }
        // --- END INITIALIZATION CHECK ---
        LogToFile("ShowOverlayOnMonitor START");
        LogToFile("ShowOverlayOnMonitor called for index %d", monitorIndex);
        HRESULT hr = S_OK;

        // --- Cleanup previous instance ---
        hr = HideOverlay();
        if (FAILED(hr)) 
        {
            LogToFile("ShowOverlayOnMonitor: HideOverlay failed with HRESULT: 0x%X", hr);
            return hr;
        }

        // --- Input Validation ---
        if (!text) 
        {
            LogToFile("ShowOverlayOnMonitor error: text pointer is NULL.");
            return E_POINTER;
        }

        if (borderW < 0) borderW = 0;
        if (fSize <= 0) fSize = 1;

        // --- Update Global State ---
        try 
        {
            g_overlayText = text; // Assign C-style string to std::wstring
        }
        catch (const std::exception& e) {
            LogToFile("ShowOverlayOnMonitor: EXCEPTION assigning text: %s", e.what());
            return E_FAIL; // Indicate failure
        }
        catch (...) {
            LogToFile("ShowOverlayOnMonitor: UNKNOWN EXCEPTION assigning text!");
            return E_FAIL;
        }
        // --- End Text Assignment Change ---

        g_showOverlayText = showText;
        g_borderWidth = borderW;
        g_borderColor = RGB(r, g, b);
        g_fontSize = fSize;
        g_textAlignment = alignment;

        // --- Register Window Class ---
        HINSTANCE hInstance = GetModuleHandle(NULL);
        if (!hInstance) 
        {
            DWORD error = GetLastError();
            LogToFile("ShowOverlayOnMonitor: GetModuleHandle(NULL) failed. Error: %lu", error);
            return HRESULT_FROM_WIN32(error);
        }
        hr = RegisterOverlayWindowClass(hInstance);
        if (FAILED(hr)) 
        {
            LogToFile("ShowOverlayOnMonitor: RegisterOverlayWindowClass failed with HRESULT: 0x%X", hr);
            return COVINCE_E_CLASS_REGISTRATION_FAILED;
        }

        // --- Find Specific Monitor ---
        std::vector<HMONITOR> monitors;
        LPARAM lParamMon = reinterpret_cast<LPARAM>(&monitors);
        EnumDisplayMonitors(NULL, NULL, [](HMONITOR hMon, HDC, LPRECT, LPARAM dwData) -> BOOL {
            auto* pMons = reinterpret_cast<std::vector<HMONITOR>*>(dwData);
            pMons->push_back(hMon);
            return TRUE;
            }, lParamMon);

        if (monitorIndex < 0 || monitorIndex >= static_cast<int>(monitors.size())) 
        {
            LogToFile("ShowOverlayOnMonitor: Invalid monitor index %d (found %zu monitors).", monitorIndex, monitors.size());
            return COVINCE_E_INVALID_MONITOR_INDEX;
        }
        HMONITOR hMonitor = monitors[monitorIndex];

        MONITORINFO mi = { sizeof(MONITORINFO) };
        if (!GetMonitorInfo(hMonitor, &mi)) 
        {
            DWORD error = GetLastError();
            LogToFile("ShowOverlayOnMonitor: GetMonitorInfo failed for index %d. Error: %lu", monitorIndex, error);
            return HRESULT_FROM_WIN32(error);
        }

        // --- Create Single Window ---
        HWND hwnd = CreateWindowExW(
            WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOACTIVATE, // Added NOACTIVATE
            WND_CLASS_NAME, L"Overlay", WS_POPUP,
            mi.rcMonitor.left, mi.rcMonitor.top,
            mi.rcMonitor.right - mi.rcMonitor.left,
            mi.rcMonitor.bottom - mi.rcMonitor.top,
            NULL, NULL, hInstance, NULL
        );

        if (!hwnd) 
        {
            DWORD error = GetLastError();
            LogToFile("ShowOverlayOnMonitor: CreateWindowExW failed. Error: %lu", error);
            return COVINCE_E_WINDOW_CREATION_FAILED; // Or HRESULT_FROM_WIN32(error)
        }

        ShowWindow(hwnd, SW_SHOWNA);
        InvalidateRect(hwnd, NULL, TRUE); // Request a repaint immediately

        // --- Finalize State ---
        g_overlayWindows.push_back(hwnd); // Add the single window
        g_overlayShown = true;
        LogToFile("ShowOverlayOnMonitor created window %p on monitor %d. Returning S_OK.", hwnd, monitorIndex);
        return S_OK;
    }

    COVINCEWINDOWSOVERLAY_API HRESULT __cdecl HideOverlay()
    {
        // --- ADD INITIALIZATION CHECK AT THE VERY START ---
        if (!g_dllInitializedOk.load(std::memory_order_acquire)) // Use acquire load for visibility
        {
            OutputDebugStringA("HideOverlay called but DLL not initialized!\n"); // Safer log here
            return COVINCE_E_NOT_INITIALIZED;
        }
        // --- END INITIALIZATION CHECK ---
        LogToFile("HideOverlay START");

        // --- Handle "Already Hidden" State ---
        // Check the atomic flag first for quick exit
        if (!g_overlayShown.load()) {
            LogToFile("HideOverlay: Overlay already hidden or not initialized.");

            // Optional: You could add checks/cleanup here for consistency if pointers/cache
            // somehow got populated without g_overlayShown being true, but it might
            // indicate logic errors elsewhere. Let's keep it simple for now.
            // If you need it:
            /*
            EnterCriticalSection(&g_pointersCritSec);
            try { if (!g_remotePointers.empty()) g_remotePointers.clear(); } catch(...) {}
            LeaveCriticalSection(&g_pointersCritSec);
            EnterCriticalSection(&g_imageCacheCritSec);
            try { if (!g_imageCache.empty()) ClearImageCacheInternal(); } catch(...) {}
            LeaveCriticalSection(&g_imageCacheCritSec);
            */
            return S_OK; // Nothing to do
        }

        // --- Destroy Windows (No lock needed for this part) ---
        LogToFile("HideOverlay: Destroying %zu windows.", g_overlayWindows.size());
        for (HWND hwnd : g_overlayWindows)
        {
            if (IsWindow(hwnd))
            {
                DestroyWindow(hwnd);
            }
        }
        g_overlayWindows.clear(); // Clear window list AFTER destroying


        // --- Cleanup Pointer Map ---
        bool pointersWereCleared = false;
        EnterCriticalSection(&g_pointersCritSec);
        try {
            if (!g_remotePointers.empty()) {
                g_remotePointers.clear();
                pointersWereCleared = true;
                // Logging moved after LeaveCriticalSection
            }
        }
        catch (const std::exception& e) {
            // Log error *after* leaving critical section if possible
            LogToFile("HideOverlay: EXCEPTION clearing pointer map: %s", e.what());
        }
        catch (...) {
            LogToFile("HideOverlay: UNKNOWN EXCEPTION clearing pointer map!");
        }
        LeaveCriticalSection(&g_pointersCritSec); // Ensure Leave is always called

        if (pointersWereCleared) {
            LogToFile("HideOverlay: Cleared pointer map.");
        }

        // --- Cleanup Image Cache ---
        bool cacheWasCleared = false;
        EnterCriticalSection(&g_imageCacheCritSec);
        try {
            if (!g_imageCache.empty()) {
                ClearImageCacheInternal(); // Helper deletes bitmaps AND logs clearing now
                cacheWasCleared = true;
            }
        }
        catch (const std::exception& e) {
            LogToFile("HideOverlay: EXCEPTION clearing image cache: %s", e.what());
        }
        catch (...) {
            LogToFile("HideOverlay: UNKNOWN EXCEPTION clearing image cache!");
        }
        LeaveCriticalSection(&g_imageCacheCritSec); // Ensure Leave is always called

        // --- ------------------------------------ ---

        g_overlayShown = false; // Set flag AFTER cleanup attempts

        LogToFile("HideOverlay completed.");
        return S_OK;
    }


    // --- Helper for update functions ---
    HRESULT UpdateOverlayProperty(std::function<void()> updateAction)
    {
        LogToFile("UpdateOverlayProperty: START");
        if (!g_overlayShown.load() || g_overlayWindows.empty()) {
            LogToFile("UpdateOverlayProperty: Overlay not initialized.");
            return COVINCE_E_NOT_INITIALIZED;
        }

        updateAction(); // Perform the specific state update

        // Invalidate all windows
        for (HWND hwnd : g_overlayWindows) {
            if (IsWindow(hwnd)) {
                InvalidateRect(hwnd, NULL, TRUE);
            }
        }
        LogToFile("UpdateOverlayProperty completed and invalidated windows. Returning S_OK.");
        return S_OK;
    }

    // --- Update Functions Implementation ---
    COVINCEWINDOWSOVERLAY_API HRESULT __cdecl ShowOverlayText(bool show)
    {
        // --- ADD INITIALIZATION CHECK AT THE VERY START ---
        if (!g_dllInitializedOk.load(std::memory_order_acquire)) // Use acquire load for visibility
        {
            OutputDebugStringA("ShowOverlayText called but DLL not initialized!\n"); // Safer log here
            return COVINCE_E_NOT_INITIALIZED;
        }
        // --- END INITIALIZATION CHECK ---
        LogToFile("ShowOverlayText called with show = %d", show);
        return UpdateOverlayProperty([&]() {
            g_showOverlayText = show;
            });
    }

    COVINCEWINDOWSOVERLAY_API HRESULT __cdecl UpdateOverlayText(const wchar_t* text)
    {
        // --- ADD INITIALIZATION CHECK AT THE VERY START ---
        if (!g_dllInitializedOk.load(std::memory_order_acquire)) // Use acquire load for visibility
        {
            OutputDebugStringA("UpdateOverlayText called but DLL not initialized!\n"); // Safer log here
            return COVINCE_E_NOT_INITIALIZED;
        }
        // --- END INITIALIZATION CHECK ---
        LogToFile("UpdateOverlayText START");
        if (!text) {
            LogToFile("UpdateOverlayText error: text pointer is NULL.");
            return E_POINTER;
        }

        // Optional: You might still want a general sanity check on length,
        // but it's not tied to a fixed buffer size anymore.
        /*
        size_t textLen = wcslen(text);
        if (textLen > 4096) { // Example: Arbitrary large limit
            LogToFile("UpdateOverlayText error: text length (%zu) exceeds arbitrary limit.", textLen);
            return E_INVALIDARG; // Or a custom HRESULT
        }
        */

        // Use UpdateOverlayProperty, passing a lambda that assigns to the std::wstring
        return UpdateOverlayProperty([&]() {
            try {
                // Use the std::wstring assignment operator
                g_overlayText = text;
                // Optional logging inside lambda if needed, using LogToFile carefully
                // LogToFile("Updated g_overlayText");
            }
            catch (const std::bad_alloc& e) {
                // Handle potential memory allocation failure during wstring assignment
                LogToFile("UpdateOverlayText: EXCEPTION assigning text (bad_alloc): %s", e.what());
                // Cannot easily return error HRESULT from lambda within UpdateOverlayProperty
                // Maybe set a global error flag if needed? Or just log.
            }
            catch (const std::exception& e) {
                LogToFile("UpdateOverlayText: EXCEPTION assigning text: %s", e.what());
            }
            catch (...) {
                LogToFile("UpdateOverlayText: UNKNOWN EXCEPTION assigning text!");
            }
            });
    }

    COVINCEWINDOWSOVERLAY_API HRESULT __cdecl UpdateOverlayFontSize(int fSize)
    {
        // --- ADD INITIALIZATION CHECK AT THE VERY START ---
        if (!g_dllInitializedOk.load(std::memory_order_acquire)) // Use acquire load for visibility
        {
            OutputDebugStringA("UpdateOverlayFontSize called but DLL not initialized!\n"); // Safer log here
            return COVINCE_E_NOT_INITIALIZED;
        }
        // --- END INITIALIZATION CHECK ---
        LogToFile("UpdateOverlayFontSize called with fSize = %d", fSize);
        if (fSize <= 0) fSize = 1; // Basic validation
        return UpdateOverlayProperty([&]() {
            g_fontSize = fSize;
            });
    }

    COVINCEWINDOWSOVERLAY_API HRESULT __cdecl UpdateOverlayWidth(int bWidth)
    {
        // --- ADD INITIALIZATION CHECK AT THE VERY START ---
        if (!g_dllInitializedOk.load(std::memory_order_acquire)) // Use acquire load for visibility
        {
            OutputDebugStringA("UpdateOverlayWidth called but DLL not initialized!\n"); // Safer log here
            return COVINCE_E_NOT_INITIALIZED;
        }
        // --- END INITIALIZATION CHECK ---
        LogToFile("UpdateOverlayWidth called with bWidth = %d", bWidth);
        if (bWidth < 0) bWidth = 0; // Basic validation
        return UpdateOverlayProperty([&]() {
            g_borderWidth = bWidth;
            });
    }

    COVINCEWINDOWSOVERLAY_API HRESULT __cdecl UpdateOverlayColor(int r, int g, int b)
    {
        // --- ADD INITIALIZATION CHECK AT THE VERY START ---
        if (!g_dllInitializedOk.load(std::memory_order_acquire)) // Use acquire load for visibility
        {
            OutputDebugStringA("UpdateOverlayColor called but DLL not initialized!\n"); // Safer log here
            return COVINCE_E_NOT_INITIALIZED;
        }
        // --- END INITIALIZATION CHECK ---
        LogToFile("UpdateOverlayColor called with RGB(%d, %d, %d)", r, g, b);
        return UpdateOverlayProperty([&]() {
            g_borderColor = RGB(r, g, b);
            });
    }

    COVINCEWINDOWSOVERLAY_API HRESULT __cdecl UpdateOverlayAlignment(CoVinceOverlayTextAlignment alignment)
    {
        // --- ADD INITIALIZATION CHECK AT THE VERY START ---
        if (!g_dllInitializedOk.load(std::memory_order_acquire)) // Use acquire load for visibility
        {
            OutputDebugStringA("UpdateOverlayAlignment called but DLL not initialized!\n"); // Safer log here
            return COVINCE_E_NOT_INITIALIZED;
        }
        // --- END INITIALIZATION CHECK ---
        LogToFile("UpdateOverlayAlignment called with alignment = %d", alignment);
        return UpdateOverlayProperty([&]() {
            g_textAlignment = alignment; // This automatically uses atomic store
            });
    }


// --- Pointer API Functions (Modified for Image Path) ---
    COVINCEWINDOWSOVERLAY_API HRESULT __cdecl AddOrUpdatePointer(const wchar_t* userId, const wchar_t* userName, int x, int y, const wchar_t* imagePath, int desiredWidth, int desiredHeight)  // Desired Size
    {
        // --- ADD INITIALIZATION CHECK AT THE VERY START ---
        if (!g_dllInitializedOk.load(std::memory_order_acquire)) // Use acquire load for visibility
        {
            OutputDebugStringA("AddOrUpdatePointer called but DLL not initialized!\n"); // Safer log here
            return COVINCE_E_NOT_INITIALIZED;
        }
        // --- END INITIALIZATION CHECK ---
        // --- Input Validation ---
        if (!userId || !userName || !imagePath) {
            LogToFile("AddOrUpdatePointer ERROR: Null input pointer provided.");
            return E_POINTER;
        }
        if (desiredWidth <= 0 || desiredHeight <= 0) {
            LogToFile("AddOrUpdatePointer ERROR: Invalid desired dimensions (%d x %d) for UserID '%ls'.", desiredWidth, desiredHeight, userId);
            return E_INVALIDARG;
        }

        LogToFile("AddOrUpdatePointer START - UserID: '%ls', Img: '%ls', Pos: (%d,%d), Size: (%dx%d)",
            userId, imagePath, x, y, desiredWidth, desiredHeight);

        HRESULT hr = S_OK;
        bool imageLoadedOrExisted = false;
        std::wstring wsImagePath = imagePath; // Convert path for map key/comparison

        // --- Load/Cache Image (Protected by Image Cache Critical Section) ---
        EnterCriticalSection(&g_imageCacheCritSec);
        try
        {
            auto it = g_imageCache.find(wsImagePath);
            if (it == g_imageCache.end()) { // Not in cache
                LogToFile("AddOrUpdatePointer: Image '%ls' not in cache. Loading...", wsImagePath.c_str());
                Bitmap* newBitmap = nullptr;
                try {
                    // Use Bitmap constructor that takes path and useEmbeddedColorManagement flag
                    newBitmap = new Bitmap(wsImagePath.c_str(), FALSE); // Create on heap
                }
                catch (const std::exception& e) {
                    LogToFile("AddOrUpdatePointer: EXCEPTION during Bitmap creation for '%ls': %s", wsImagePath.c_str(), e.what());
                    hr = E_FAIL;
                }
                catch (...) {
                    LogToFile("AddOrUpdatePointer: UNKNOWN EXCEPTION during Bitmap creation for '%ls'", wsImagePath.c_str());
                    hr = E_FAIL;
                }

                // Check GDI+ status *after* potential constructor completion/exception
                if (SUCCEEDED(hr)) {
                    if (newBitmap && newBitmap->GetLastStatus() == Ok) {
                        g_imageCache[wsImagePath] = newBitmap; // Store pointer in cache
                        imageLoadedOrExisted = true;
                        LogToFile("Loaded image '%ls' into cache.", wsImagePath.c_str());

                        if (g_imageCache.size() > MAX_CACHE_ITEMS) {
                            LogToFile("Image cache size (%zu) exceeds limit (%zu). Trimming...", g_imageCache.size(), MAX_CACHE_ITEMS);
                            size_t itemsToDelete = g_imageCache.size() / 2; // Or just trim down to MAX_CACHE_ITEMS
                            auto it = g_imageCache.begin();
                            for (size_t i = 0; i < itemsToDelete && it != g_imageCache.end(); ++i) {
                                LogToFile("Trimming image cache: Deleting '%ls'", it->first.c_str());
                                delete it->second; // Delete the bitmap
                                it = g_imageCache.erase(it); // Erase element and advance iterator correctly
                            }
                            LogToFile("Image cache trimmed. New size: %zu", g_imageCache.size());
                        }
                    }
                    else {
                        Status loadStatus = newBitmap ? newBitmap->GetLastStatus() : GenericError; // Get status or error code
                        LogToFile("Failed to load image '%ls'. GDI+ Status: %d", wsImagePath.c_str(), loadStatus);
                        delete newBitmap; // Important: delete pointer if constructor succeeded but status is bad, or if constructor returned null but allocated something partially? Safer to delete.
                        hr = E_FAIL;
                    }
                }
                else {
                    // Exception occurred during Bitmap creation, ensure pointer is deleted if somehow non-null
                    delete newBitmap;
                }
            }
            else {
                imageLoadedOrExisted = true; // Image already exists in cache
                // LogToFile("AddOrUpdatePointer: Image '%ls' found in cache.", wsImagePath.c_str());
            }
        }
        catch (...) { // Catch potential exceptions from std::map or std::wstring itself
            LeaveCriticalSection(&g_imageCacheCritSec); // Ensure leave on exception
            LogToFile("AddOrUpdatePointer: *** CRITICAL EXCEPTION in Image Cache block! ***");
            return E_FAIL; // Return failure
        }
        LeaveCriticalSection(&g_imageCacheCritSec); // Leave normally
        // --- Image Cache Unlocked ---

        if (FAILED(hr)) {
            LogToFile("AddOrUpdatePointer: Bailing out due to image load/cache failure.");
            return hr;
        }

        // --- Update Pointer Map (Protected by Pointers Critical Section) ---
        EnterCriticalSection(&g_pointersCritSec);
        try
        {
            // Only proceed if image is ready
            // No need for imageLoadedOrExisted check here due to FAILED(hr) check above
            RemotePointer& rp = g_remotePointers[userId]; // Get reference or create new entry
            rp.userName = userName;
            rp.position = { x, y };
            rp.imagePath = wsImagePath; // Store the path (key to the image cache)
            rp.displayWidth = desiredWidth;
            rp.displayHeight = desiredHeight;
            // Log moved outside lock
        }
        catch (const std::exception& e) {
            LeaveCriticalSection(&g_pointersCritSec); // Ensure leave on exception
            LogToFile("AddOrUpdatePointer: EXCEPTION Failed to update pointer map for '%ls': %s", userId, e.what());
            return E_FAIL; // Return failure
        }
        catch (...) {
            LeaveCriticalSection(&g_pointersCritSec); // Ensure leave on exception
            LogToFile("AddOrUpdatePointer: UNKNOWN EXCEPTION updating pointer map for '%ls'", userId);
            return E_FAIL; // Return failure
        }
        LeaveCriticalSection(&g_pointersCritSec); // Leave normally
        LogToFile("Updated pointer map for '%ls'.", userId); // Log success outside lock
        // --- Pointers Unlocked ---


        // --- Trigger Repaint ---
        if (g_overlayShown.load()) {
            LogToFile("AddOrUpdatePointer: Triggering InvalidateRect.");
            for (HWND hwnd : g_overlayWindows) {
                if (IsWindow(hwnd)) { InvalidateRect(hwnd, NULL, TRUE); }
            }
        }
        else {
            LogToFile("AddOrUpdatePointer: Overlay not shown, repaint not triggered.");
        }

        return S_OK; // Return success
    }

    COVINCEWINDOWSOVERLAY_API HRESULT __cdecl RemovePointer(const wchar_t* userId)
    {
        // --- ADD INITIALIZATION CHECK AT THE VERY START ---
        if (!g_dllInitializedOk.load(std::memory_order_acquire)) // Use acquire load for visibility
        {
            OutputDebugStringA("RemovePointer called but DLL not initialized!\n"); // Safer log here
            return COVINCE_E_NOT_INITIALIZED;
        }
        // --- END INITIALIZATION CHECK ---
        LogToFile("RemovePointer START for UserID: '%ls'", userId ? userId : L"NULL");
        if (!userId) {
            LogToFile("RemovePointer ERROR: Null userId provided.");
            return E_POINTER;
        }

        size_t erasedCount = 0;

        // --- Remove from Pointer Map (Protected by Critical Section) ---
        EnterCriticalSection(&g_pointersCritSec); // *** ENTER CS ***
        try
        {
            // Attempt to erase the element from the map
            erasedCount = g_remotePointers.erase(userId);
            // Logging about success/failure moved after Leave
        }
        catch (const std::exception& e)
        {
            // Log error if std::map::erase throws (unlikely for standard types unless memory issue)
            LeaveCriticalSection(&g_pointersCritSec); // *** LEAVE CS on exception ***
            LogToFile("RemovePointer: EXCEPTION during map erase for '%ls': %s", userId, e.what());
            // Decide on return code - maybe still try to repaint? Returning error seems appropriate.
            return E_FAIL;
        }
        catch (...)
        {
            LeaveCriticalSection(&g_pointersCritSec); // *** LEAVE CS on exception ***
            LogToFile("RemovePointer: UNKNOWN EXCEPTION during map erase for '%ls'", userId);
            return E_FAIL;
        }
        LeaveCriticalSection(&g_pointersCritSec); // *** LEAVE CS on normal path ***
        // --- Pointer Map Unlocked ---

        // Log result after lock is released
        LogToFile(erasedCount > 0 ? "Removed pointer '%ls' from map." : "Pointer '%ls' not found in map.", userId);

        // --- Trigger Repaint ---
        // We don't automatically remove from the image cache when a single pointer is removed
        // using the simple caching strategy (only clear cache in HideOverlay/ClearAllPointers).
        if (erasedCount > 0 && g_overlayShown.load()) {
            LogToFile("RemovePointer: Triggering InvalidateRect.");
            for (HWND hwnd : g_overlayWindows) {
                if (IsWindow(hwnd)) { InvalidateRect(hwnd, NULL, TRUE); }
            }
        }

        // It's generally fine to return S_OK even if the pointer wasn't found (idempotent delete)
        return S_OK;
    }

    COVINCEWINDOWSOVERLAY_API HRESULT __cdecl ClearAllPointers()
    {
        // --- ADD INITIALIZATION CHECK AT THE VERY START ---
        if (!g_dllInitializedOk.load(std::memory_order_acquire)) // Use acquire load for visibility
        {
            OutputDebugStringA("ClearAllPointers called but DLL not initialized!\n"); // Safer log here
            return COVINCE_E_NOT_INITIALIZED;
        }
        // --- END INITIALIZATION CHECK ---
        LogToFile("ClearAllPointers START");
        bool hadPointers = false;
        HRESULT pointerClearHr = S_OK;
        HRESULT cacheClearHr = S_OK;

        // --- Clear Pointer Map ---
        EnterCriticalSection(&g_pointersCritSec);
        try {
            hadPointers = !g_remotePointers.empty();
            if (hadPointers) g_remotePointers.clear();
        }
        catch (const std::exception& e) {
            LogToFile("ClearAllPointers: EXCEPTION clearing pointer map: %s", e.what());
            pointerClearHr = E_FAIL; // Mark error but continue
        }
        catch (...) {
            LogToFile("ClearAllPointers: UNKNOWN EXCEPTION clearing pointer map!");
            pointerClearHr = E_FAIL; // Mark error but continue
        }
        LeaveCriticalSection(&g_pointersCritSec); // Leave AFTER try/catch
        if (SUCCEEDED(pointerClearHr) && hadPointers) LogToFile("Cleared pointer map.");

        // --- Clear Image Cache ---
        EnterCriticalSection(&g_imageCacheCritSec);
        try {
            if (!g_imageCache.empty()) {
                ClearImageCacheInternal(); // Helper deletes bitmaps and logs
            }
            else {
                // LogToFile("ClearAllPointers: Image cache already empty.");
            }
        }
        catch (const std::exception& e) {
            LogToFile("ClearAllPointers: EXCEPTION clearing image cache: %s", e.what());
            cacheClearHr = E_FAIL; // Mark error
        }
        catch (...) {
            LogToFile("ClearAllPointers: UNKNOWN EXCEPTION clearing image cache!");
            cacheClearHr = E_FAIL; // Mark error
        }
        LeaveCriticalSection(&g_imageCacheCritSec); // Leave AFTER try/catch

        // --- Trigger Repaint ---
        if (hadPointers && g_overlayShown.load()) {
            LogToFile("ClearAllPointers: Triggering InvalidateRect.");
            for (HWND hwnd : g_overlayWindows) {
                if (IsWindow(hwnd)) { InvalidateRect(hwnd, NULL, TRUE); }
            }
        }

        // Return success unless something failed during cleanup
        return FAILED(pointerClearHr) || FAILED(cacheClearHr) ? E_FAIL : S_OK;
    }

#ifdef __cplusplus
}

#endif

static BOOL InitializeCriticalSections() 
{
    OutputDebugStringA("===> Initializing Critical Sections...\n");
    // InitializeCriticalSectionAndSpinCount returns BOOL (non-zero for success, zero for failure)
    // We want to return TRUE only if ALL succeed.
    BOOL cs1_ok = InitializeCriticalSectionAndSpinCount(&g_pointersCritSec, 0x00000400);
    BOOL cs2_ok = InitializeCriticalSectionAndSpinCount(&g_imageCacheCritSec, 0x00000400);
    BOOL cs3_ok = InitializeCriticalSectionAndSpinCount(&g_logCritSec, 0x00000400);

    if (cs1_ok && cs2_ok && cs3_ok) 
    {
        OutputDebugStringA("===> Critical Sections Initialized OK.\n");
        return TRUE; // All succeeded
    }
    else 
    {
        // Log which one(s) failed if possible/needed for more detail
        if (!cs1_ok) OutputDebugStringA("===> g_pointersCritSec FAILED to Initialize!\n");
        if (!cs2_ok) OutputDebugStringA("===> g_imageCacheCritSec FAILED to Initialize!\n");
        if (!cs3_ok) OutputDebugStringA("===> g_logCritSec FAILED to Initialize!\n");
        OutputDebugStringA("===> Critical Sections FAILED to Initialize!\n");
        return FALSE; // At least one failed
    }
}

static void DeleteCriticalSections() 
{
    OutputDebugStringA("===> Deleting Critical Sections...\n");
    DeleteCriticalSection(&g_pointersCritSec);
    DeleteCriticalSection(&g_imageCacheCritSec);
    DeleteCriticalSection(&g_logCritSec);
    OutputDebugStringA("===> Critical Sections Deleted.\n");
}


//Maak de log in dezelfde forlder als  waar de images komen te staan, via de global constants
// Refined DllMain using only OutputDebugStringA for its own tracing
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        { 
            // This *should* appear now based on the minimal test
            OutputDebugStringA("===> 1  DLL_PROCESS_ATTACH Entry\n");

            // --- Debug GDI+ Initialization ---
            OutputDebugStringA("===> DBG: Attempting GdiplusStartup...\n");
            Status gdiplusStatus = GdiplusStartup(&g_gdiplusToken, &g_gdiplusStartupInput, NULL);
            if (gdiplusStatus == Ok)
            {
                OutputDebugStringA("===> DBG: GdiplusStartup SUCCEEDED.\n");
            }
            else
            {
                // Log the specific error before returning FALSE
                char errMsg[128];
                sprintf_s(errMsg, sizeof(errMsg), "===> DBG: GdiplusStartup FAILED! Status Code: %d. Returning FALSE from DllMain.\n", gdiplusStatus);
                OutputDebugStringA(errMsg);
                return FALSE; // Exit DLL load on GDI+ failure
            }

            // --- Debug Critical Section Initialization ---
            OutputDebugStringA("===> DBG: Attempting InitializeCriticalSections...\n");
            BOOL csInitOk = InitializeCriticalSections(); // Ensure this function exists and is correct
            if (csInitOk)
            {
                OutputDebugStringA("===> DBG: InitializeCriticalSections SUCCEEDED.\n");
            }
            else
            {
                // Log the failure before returning FALSE
                OutputDebugStringA("===> DBG: InitializeCriticalSections FAILED! Returning FALSE from DllMain.\n");
                return FALSE; // Exit DLL load on CS failure
            }

            // If both succeeded, we should reach here
            OutputDebugStringA("===> DBG: All initializations passed. Setting g_dllInitializedOk.\n");
            g_dllInitializedOk = true; // Set flag only if everything succeeded
            OutputDebugStringA("===> 5  DLL_PROCESS_ATTACH Exit (Success)\n"); // Original log message
            break; // Break from case DLL_PROCESS_ATTACH

            /*
            OutputDebugStringA("===> 1  DLL_PROCESS_ATTACH Entry\n");

            Status gdiplusStatus;
            OutputDebugStringA("===> 2  Calling GdiplusStartup...\n");
            gdiplusStatus = GdiplusStartup(&g_gdiplusToken, &g_gdiplusStartupInput, NULL);
            OutputDebugStringA("===> 3  GdiplusStartup Returned\n");
            if (gdiplusStatus == Ok) {
                OutputDebugStringA("===> 4A GdiplusStartup Status: OK\n");
            }
            else {
                char errMsg[100];
                sprintf_s(errMsg, "===> 4B GdiplusStartup Status: FAILED! Code: %d\n", gdiplusStatus);
                OutputDebugStringA(errMsg);
                // return FALSE; // Optional: Fail DLL load if GDI+ failed
            }

            // Call the helper function
            if (!InitializeCriticalSections()) 
            {
                OutputDebugStringA("!!! DllMain returning FALSE due to Critical Section init failure!\n");
                return FALSE; // Return FALSE from DllMain to indicate DLL load failure
                // This prevents the DLL from being used if essential sync primitives fail
            }

            OutputDebugStringA("===> 5  DLL_PROCESS_ATTACH Exit\n");
            g_dllInitializedOk = true;
            break; // Break remains inside the block
            */
        } // <-- ADD closing brace HERE

        case DLL_THREAD_ATTACH:
            // Avoid complex operations here
            break;

        case DLL_THREAD_DETACH:
            // Avoid complex operations here
            break;

        case DLL_PROCESS_DETACH:
        {
            OutputDebugStringA("===> DBG: DLL_PROCESS_DETACH received.\n");

            HideOverlay(); // Clean up windows and associated maps/cache

            // Shutdown Logging - Calls the helper which now uses CS internally
            ShutdownLoggingInternal();

            DeleteCriticalSections(); // Call the cleanup helper

            OutputDebugStringA("===> B Calling GdiplusShutdown...\n");
            GdiplusShutdown(g_gdiplusToken);
            OutputDebugStringA("===> C GdiplusShutdown Returned\n");
            OutputDebugStringA("===> D DLL_PROCESS_DETACH Exit\n");
            break;
        } // <-- Add closing brace HERE
    } // End switch
    return TRUE; // Must return TRUE for successful attach
}