#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <oleauto.h>
#include <stdio.h>
#include <string.h>
#include <wbemidl.h>

#define IDC_OS_LABEL 101
#define IDC_OS_VALUE 102
#define IDC_CPU_LABEL 103
#define IDC_CPU_VALUE 104
#define IDC_GPU_LABEL 105
#define IDC_GPU_VALUE 106
#define IDC_RAM_LABEL 107
#define IDC_RAM_VALUE 108

static HFONT g_title_font = NULL;
static HFONT g_text_font = NULL;
static HFONT g_label_font = NULL;
static HBRUSH g_background_brush = NULL;

static void set_control_text(HWND window, int control_id, const char *text)
{
    SetWindowTextA(GetDlgItem(window, control_id), text);
}

static int read_registry_string(HKEY root, const char *subkey, const char *value_name, char *buffer, DWORD size)
{
    HKEY key = NULL;
    DWORD type = 0;
    DWORD data_size = size;

    if (RegOpenKeyExA(root, subkey, 0, KEY_READ, &key) != ERROR_SUCCESS) {
        return 0;
    }

    if (RegQueryValueExA(key, value_name, NULL, &type, (LPBYTE) buffer, &data_size) != ERROR_SUCCESS || type != REG_SZ) {
        RegCloseKey(key);
        return 0;
    }

    buffer[size - 1] = '\0';
    RegCloseKey(key);
    return 1;
}

static void get_os_text(char *buffer, size_t size)
{
    char product[256] = "Okant";
    char display_version[128] = "";
    char build[128] = "";

    read_registry_string(HKEY_LOCAL_MACHINE,
        "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
        "ProductName",
        product,
        sizeof(product));

    if (!read_registry_string(HKEY_LOCAL_MACHINE,
            "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
            "DisplayVersion",
            display_version,
            sizeof(display_version))) {
        read_registry_string(HKEY_LOCAL_MACHINE,
            "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
            "ReleaseId",
            display_version,
            sizeof(display_version));
    }

    read_registry_string(HKEY_LOCAL_MACHINE,
        "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
        "CurrentBuildNumber",
        build,
        sizeof(build));

    if (display_version[0] != '\0' && build[0] != '\0') {
        snprintf(buffer, size, "%s %s (build %s)", product, display_version, build);
    } else if (build[0] != '\0') {
        snprintf(buffer, size, "%s (build %s)", product, build);
    } else {
        snprintf(buffer, size, "%s", product);
    }
}

static void get_cpu_text(char *buffer, size_t size)
{
    if (!read_registry_string(HKEY_LOCAL_MACHINE,
            "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
            "ProcessorNameString",
            buffer,
            (DWORD) size)) {
        snprintf(buffer, size, "Okant");
    }
}

static void get_gpu_text(char *buffer, size_t size)
{
    DISPLAY_DEVICEA device;
    ZeroMemory(&device, sizeof(device));
    device.cb = sizeof(device);

    for (DWORD i = 0; EnumDisplayDevicesA(NULL, i, &device, 0); ++i) {
        if ((device.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) != 0) {
            ZeroMemory(&device, sizeof(device));
            device.cb = sizeof(device);
            continue;
        }

        if ((device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) != 0 || device.DeviceString[0] != '\0') {
            snprintf(buffer, size, "%s", device.DeviceString);
            return;
        }

        ZeroMemory(&device, sizeof(device));
        device.cb = sizeof(device);
    }

    snprintf(buffer, size, "Okant");
}

static const char *memory_type_name(int type)
{
    switch (type) {
    case 20:
        return "DDR";
    case 21:
        return "DDR2";
    case 24:
        return "DDR3";
    case 26:
        return "DDR4";
    case 34:
        return "DDR5";
    default:
        return NULL;
    }
}

static void append_type_if_missing(char *buffer, size_t size, const char *type_name)
{
    if (type_name == NULL || type_name[0] == '\0') {
        return;
    }

    if (strstr(buffer, type_name) != NULL) {
        return;
    }

    if (buffer[0] != '\0') {
        strncat(buffer, "/", size - strlen(buffer) - 1);
    }
    strncat(buffer, type_name, size - strlen(buffer) - 1);
}

static void get_ram_type_text(char *buffer, size_t size)
{
    HRESULT hr;
    IWbemLocator *locator = NULL;
    IWbemServices *services = NULL;
    IEnumWbemClassObject *enumerator = NULL;
    char types[64] = "";
    int com_initialized = 0;

    buffer[0] = '\0';

    hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    if (SUCCEEDED(hr)) {
        com_initialized = 1;
    } else if (hr != RPC_E_CHANGED_MODE) {
        return;
    }

    hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT,
        RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
    if (FAILED(hr) && hr != RPC_E_TOO_LATE) {
        if (com_initialized) {
            CoUninitialize();
        }
        return;
    }

    hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
        &IID_IWbemLocator, (LPVOID *) &locator);
    if (FAILED(hr) || locator == NULL) {
        if (com_initialized) {
            CoUninitialize();
        }
        return;
    }

    hr = locator->lpVtbl->ConnectServer(locator, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &services);
    if (FAILED(hr) || services == NULL) {
        locator->lpVtbl->Release(locator);
        if (com_initialized) {
            CoUninitialize();
        }
        return;
    }

    hr = CoSetProxyBlanket((IUnknown *) services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
        RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
    if (FAILED(hr)) {
        services->lpVtbl->Release(services);
        locator->lpVtbl->Release(locator);
        if (com_initialized) {
            CoUninitialize();
        }
        return;
    }

    hr = services->lpVtbl->ExecQuery(
        services,
        L"WQL",
        L"SELECT SMBIOSMemoryType FROM Win32_PhysicalMemory",
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
        NULL,
        &enumerator
    );

    if (SUCCEEDED(hr) && enumerator != NULL) {
        IWbemClassObject *object = NULL;
        ULONG returned = 0;

        while (enumerator->lpVtbl->Next(enumerator, WBEM_INFINITE, 1, &object, &returned) == S_OK) {
            VARIANT value;
            VariantInit(&value);

            hr = object->lpVtbl->Get(object, L"SMBIOSMemoryType", 0, &value, NULL, NULL);
            if (SUCCEEDED(hr) && (value.vt == VT_I4 || value.vt == VT_UI4 || value.vt == VT_I2 || value.vt == VT_UI2)) {
                int type = (value.vt == VT_I4 || value.vt == VT_I2) ? value.intVal : (int) value.uintVal;
                append_type_if_missing(types, sizeof(types), memory_type_name(type));
            }

            VariantClear(&value);
            object->lpVtbl->Release(object);
        }
    }

    if (enumerator != NULL) {
        enumerator->lpVtbl->Release(enumerator);
    }
    services->lpVtbl->Release(services);
    locator->lpVtbl->Release(locator);
    if (com_initialized) {
        CoUninitialize();
    }

    if (types[0] != '\0') {
        snprintf(buffer, size, "%s", types);
    }
}

static void get_ram_text(char *buffer, size_t size)
{
    MEMORYSTATUSEX status;
    char ram_type[64];
    ZeroMemory(&status, sizeof(status));
    status.dwLength = sizeof(status);

    if (!GlobalMemoryStatusEx(&status)) {
        snprintf(buffer, size, "Okant");
        return;
    }

    double gib = (double) status.ullTotalPhys / 1024.0 / 1024.0 / 1024.0;
    get_ram_type_text(ram_type, sizeof(ram_type));
    if (ram_type[0] != '\0') {
        snprintf(buffer, size, "%.1f GB %s", gib, ram_type);
    } else {
        snprintf(buffer, size, "%.1f GB", gib);
    }
}

static HWND add_label(HWND parent, int control_id, int x, int y, int w, int h, const char *text, HFONT font, DWORD style)
{
    HWND control = CreateWindowExA(
        0,
        "STATIC",
        text,
        WS_CHILD | WS_VISIBLE | style,
        x, y, w, h,
        parent,
        (HMENU) (INT_PTR) control_id,
        GetModuleHandleA(NULL),
        NULL
    );

    SendMessageA(control, WM_SETFONT, (WPARAM) font, TRUE);
    return control;
}

static void draw_rounded_panel(HDC dc, const RECT *rect, COLORREF fill, COLORREF border)
{
    HPEN pen = CreatePen(PS_SOLID, 1, border);
    HBRUSH brush = CreateSolidBrush(fill);
    HPEN old_pen = (HPEN) SelectObject(dc, pen);
    HBRUSH old_brush = (HBRUSH) SelectObject(dc, brush);

    RoundRect(dc, rect->left, rect->top, rect->right, rect->bottom, 18, 18);

    SelectObject(dc, old_pen);
    SelectObject(dc, old_brush);
    DeleteObject(pen);
    DeleteObject(brush);
}

static void populate_info(HWND window)
{
    char os_text[512];
    char cpu_text[512];
    char gpu_text[512];
    char ram_text[128];

    get_os_text(os_text, sizeof(os_text));
    get_cpu_text(cpu_text, sizeof(cpu_text));
    get_gpu_text(gpu_text, sizeof(gpu_text));
    get_ram_text(ram_text, sizeof(ram_text));

    set_control_text(window, IDC_OS_VALUE, os_text);
    set_control_text(window, IDC_CPU_VALUE, cpu_text);
    set_control_text(window, IDC_GPU_VALUE, gpu_text);
    set_control_text(window, IDC_RAM_VALUE, ram_text);
}

static LRESULT CALLBACK window_proc(HWND window, UINT message, WPARAM w_param, LPARAM l_param)
{
    switch (message) {
    case WM_CREATE:
    {
        HINSTANCE instance = ((LPCREATESTRUCTA) l_param)->hInstance;
        (void) instance;

        g_background_brush = CreateSolidBrush(RGB(7, 21, 28));
        g_title_font = CreateFontA(-40, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, ANSI_CHARSET,
            OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Segoe UI");
        g_text_font = CreateFontA(-22, 0, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE, ANSI_CHARSET,
            OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Segoe UI");
        g_label_font = CreateFontA(-18, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, ANSI_CHARSET,
            OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Segoe UI");

        add_label(window, 1000, 28, 26, 540, 52, "Systeminfo", g_title_font, 0);
        add_label(window, 1001, 30, 84, 700, 30, "Visar datorns OS, CPU, GPU och RAM.", g_label_font, 0);

        add_label(window, IDC_OS_LABEL, 52, 164, 120, 24, "OS", g_label_font, 0);
        add_label(window, IDC_CPU_LABEL, 52, 234, 120, 24, "CPU", g_label_font, 0);
        add_label(window, IDC_GPU_LABEL, 52, 304, 120, 24, "GPU", g_label_font, 0);
        add_label(window, IDC_RAM_LABEL, 52, 374, 120, 24, "RAM", g_label_font, 0);

        HWND os_value = CreateWindowExA(0, "STATIC", "Laser in...", WS_CHILD | WS_VISIBLE | SS_LEFT,
            188, 160, 510, 30, window, (HMENU) IDC_OS_VALUE, GetModuleHandleA(NULL), NULL);
        HWND cpu_value = CreateWindowExA(0, "STATIC", "Laser in...", WS_CHILD | WS_VISIBLE | SS_LEFT,
            188, 230, 510, 30, window, (HMENU) IDC_CPU_VALUE, GetModuleHandleA(NULL), NULL);
        HWND gpu_value = CreateWindowExA(0, "STATIC", "Laser in...", WS_CHILD | WS_VISIBLE | SS_LEFT,
            188, 300, 510, 30, window, (HMENU) IDC_GPU_VALUE, GetModuleHandleA(NULL), NULL);
        HWND ram_value = CreateWindowExA(0, "STATIC", "Laser in...", WS_CHILD | WS_VISIBLE | SS_LEFT,
            188, 370, 510, 30, window, (HMENU) IDC_RAM_VALUE, GetModuleHandleA(NULL), NULL);

        SendMessageA(os_value, WM_SETFONT, (WPARAM) g_text_font, TRUE);
        SendMessageA(cpu_value, WM_SETFONT, (WPARAM) g_text_font, TRUE);
        SendMessageA(gpu_value, WM_SETFONT, (WPARAM) g_text_font, TRUE);
        SendMessageA(ram_value, WM_SETFONT, (WPARAM) g_text_font, TRUE);

        populate_info(window);
        return 0;
    }
    case WM_CTLCOLORSTATIC:
    {
        HDC dc = (HDC) w_param;
        HWND control = (HWND) l_param;
        int control_id = GetDlgCtrlID(control);
        COLORREF text_color = RGB(241, 255, 253);

        if (control_id == 1000) {
            text_color = RGB(118, 225, 214);
        } else if (control_id == 1001) {
            text_color = RGB(155, 201, 196);
        } else if (control_id == IDC_OS_LABEL || control_id == IDC_CPU_LABEL ||
            control_id == IDC_GPU_LABEL || control_id == IDC_RAM_LABEL) {
            text_color = RGB(255, 209, 102);
        }

        SetTextColor(dc, text_color);
        SetBkMode(dc, TRANSPARENT);
        return (LRESULT) GetStockObject(NULL_BRUSH);
    }
    case WM_ERASEBKGND:
    {
        RECT rect;
        HDC dc = (HDC) w_param;
        GetClientRect(window, &rect);
        FillRect(dc, &rect, g_background_brush);

        RECT hero_rect = { 26, 22, rect.right - 26, 126 };
        RECT card_rect = { 26, 140, rect.right - 26, rect.bottom - 28 };
        draw_rounded_panel(dc, &hero_rect, RGB(10, 28, 36), RGB(28, 78, 82));
        draw_rounded_panel(dc, &card_rect, RGB(12, 31, 40), RGB(28, 78, 82));

        HPEN line_pen = CreatePen(PS_SOLID, 1, RGB(28, 78, 82));
        HPEN old_pen = (HPEN) SelectObject(dc, line_pen);

        MoveToEx(dc, 42, 210, NULL);
        LineTo(dc, rect.right - 42, 210);
        MoveToEx(dc, 42, 280, NULL);
        LineTo(dc, rect.right - 42, 280);
        MoveToEx(dc, 42, 350, NULL);
        LineTo(dc, rect.right - 42, 350);

        SelectObject(dc, old_pen);
        DeleteObject(line_pen);
        return 1;
    }
    case WM_DESTROY:
        if (g_title_font != NULL) {
            DeleteObject(g_title_font);
            g_title_font = NULL;
        }
        if (g_text_font != NULL) {
            DeleteObject(g_text_font);
            g_text_font = NULL;
        }
        if (g_label_font != NULL) {
            DeleteObject(g_label_font);
            g_label_font = NULL;
        }
        if (g_background_brush != NULL) {
            DeleteObject(g_background_brush);
            g_background_brush = NULL;
        }
        PostQuitMessage(0);
        return 0;
    default:
        return DefWindowProcA(window, message, w_param, l_param);
    }
}

int WINAPI WinMain(HINSTANCE instance, HINSTANCE previous_instance, LPSTR command_line, int show_command)
{
    const char *class_name = "VisitorSystemInfoWindow";
    WNDCLASSA window_class;
    MSG message;
    HWND window;

    (void) previous_instance;
    (void) command_line;

    ZeroMemory(&window_class, sizeof(window_class));
    window_class.lpfnWndProc = window_proc;
    window_class.hInstance = instance;
    window_class.lpszClassName = class_name;
    window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
    window_class.hbrBackground = NULL;

    if (!RegisterClassA(&window_class)) {
        return 1;
    }

    window = CreateWindowExA(
        0,
        class_name,
        "Systeminfo",
        WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_SIZEBOX,
        CW_USEDEFAULT, CW_USEDEFAULT, 760, 520,
        NULL,
        NULL,
        instance,
        NULL
    );

    if (window == NULL) {
        return 1;
    }

    ShowWindow(window, show_command);
    UpdateWindow(window);

    while (GetMessageA(&message, NULL, 0, 0) > 0) {
        TranslateMessage(&message);
        DispatchMessageA(&message);
    }

    return (int) message.wParam;
}
