#define UNICODE
#define _UNICODE
#include "LoadingWindow.h"
#include <algorithm>

static const wchar_t* kWindowClass = L"LoadingWndClass";

void LoadingWindow::Create(const std::string& title)
{
    windowTitle = std::wstring(title.begin(), title.end());

    // Register the window class (once)
    static bool registered = false;
    if (!registered)
    {
        WNDCLASS wc = {};
        wc.lpfnWndProc = LoadingWindow::ThunkProc;
        wc.hInstance = GetModuleHandle(nullptr);
        wc.lpszClassName = kWindowClass;
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        RegisterClass(&wc);
        registered = true;
    }

    hwnd = CreateWindowEx(
        0, kWindowClass, windowTitle.c_str(),
        WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME,
        CW_USEDEFAULT, CW_USEDEFAULT, 500, 200,
        nullptr, nullptr, GetModuleHandle(nullptr), this
    );

    ShowWindow(hwnd, SW_SHOWNORMAL);
    UpdateWindow(hwnd);
}

void LoadingWindow::Update(const std::string& step, const std::string& detail, float progress)
{
    currentStep = step;
    currentDetail = detail;
    currentProgress = std::clamp(progress, 0.0f, 1.0f);

    InvalidateRect(hwnd, nullptr, TRUE);
    UpdateWindow(hwnd);

    MSG msg;
    while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

void LoadingWindow::Destroy()
{
    if (hwnd)
    {
        DestroyWindow(hwnd);
        hwnd = nullptr;
    }
}

LRESULT CALLBACK LoadingWindow::ThunkProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (msg == WM_NCCREATE)
    {
        // Store 'this' in GWLP_USERDATA
        CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lParam);
        SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(cs->lpCreateParams));
        return TRUE;
    }

    LoadingWindow* self = reinterpret_cast<LoadingWindow*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    if (self)
        return self->WndProc(hWnd, msg, wParam, lParam);

    return DefWindowProc(hWnd, msg, wParam, lParam);
}

LRESULT LoadingWindow::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (msg == WM_PAINT)
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);

        RECT r;
        GetClientRect(hWnd, &r);

        std::wstring step = std::wstring(currentStep.begin(), currentStep.end());
        std::wstring detail = std::wstring(currentDetail.begin(), currentDetail.end());

        SetBkMode(hdc, TRANSPARENT);
        SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));

        RECT textRect = r;
        textRect.top += 10;
        textRect.left += 20;

        DrawTextW(hdc, step.c_str(), -1, &textRect, DT_LEFT | DT_TOP | DT_SINGLELINE);
        textRect.top += 30;
        DrawTextW(hdc, detail.c_str(), -1, &textRect, DT_LEFT | DT_TOP | DT_SINGLELINE);

        // Progress bar background
        RECT progressRect = { r.left + 20, r.bottom - 50, r.right - 20, r.bottom - 30 };
        HBRUSH bg = CreateSolidBrush(RGB(200, 200, 200));
        FillRect(hdc, &progressRect, bg);
        DeleteObject(bg);

        // Progress bar fill
        int barWidth = static_cast<int>((progressRect.right - progressRect.left) * currentProgress);
        RECT fillRect = { progressRect.left, progressRect.top, progressRect.left + barWidth, progressRect.bottom };
        HBRUSH fill = CreateSolidBrush(RGB(70, 140, 255));
        FillRect(hdc, &fillRect, fill);
        DeleteObject(fill);

        EndPaint(hWnd, &ps);
        return 0;
    }

    return DefWindowProc(hWnd, msg, wParam, lParam);
}
