#include <windows.h>
#include <shellapi.h>
#include <ctime>
#include <string>

#define TIMER_ID   1
#define TRAY_ID    1
#define WM_TRAYICON (WM_APP + 1)

// ---------- window constants ----------
static const int WIN_W = 150;
static const int WIN_H = 70;
static const int CORNER_RADIUS = 14;
static const BYTE BASE_ALPHA   = 120;   // ~78% opaque
static const BYTE HOVER_ALPHA  = 120;   // ~94% opaque on mouse hover

// ---------- forward decls ----------
void  AddTrayIcon(HWND hwnd);
void  RemoveTrayIcon(HWND hwnd);
void  ShowTrayContextMenu(HWND hwnd);
HICON CreateClockIcon();
void  ApplyRoundedCorners(HWND hwnd, int w, int h, int r);
void  SetWindowAlpha(HWND hwnd, BYTE alpha);
void  PinToTop(HWND hwnd);

// ===================================================================
//  Window procedure
// ===================================================================
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    static HFONT hFontTime;
    // static HFONT hFontDate;
    static bool  isDragging   = false;
    static POINT dragStart    = {0, 0};
    static bool  isMouseOver  = false;

    switch (uMsg) {

    // ---- create ------------------------------------------------
    case WM_CREATE: {
        // fonts
        hFontTime = CreateFontW(
            45, 0, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE,
            DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
            CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Segoe UI");

        // hFontDate = CreateFontW(
        //     18, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
        //     DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
        //     CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Segoe UI");

        SetTimer(hwnd, TIMER_ID, 1000, NULL);
        ApplyRoundedCorners(hwnd, WIN_W, WIN_H, CORNER_RADIUS);
        AddTrayIcon(hwnd);

        // start tracking mouse for hover effect
        TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT) };
        tme.dwFlags   = TME_HOVER | TME_LEAVE;
        tme.hwndTrack = hwnd;
        tme.dwHoverTime = 100;
        TrackMouseEvent(&tme);
        return 0;
    }

    // ---- timer → repaint ---------------------------------------
    case WM_TIMER:
        if (IsWindowVisible(hwnd))
            PinToTop(hwnd);          // re-assert "always on top" every tick
        InvalidateRect(hwnd, NULL, FALSE);
        return 0;

    // ---- paint -------------------------------------------------
    case WM_PAINT: {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        RECT rc;
        GetClientRect(hwnd, &rc);
        int w = rc.right  - rc.left;
        int h = rc.bottom - rc.top;

        // -- gradient background --
        // dark charcoal #2a2a2a → slightly lighter #3c3c3c
        for (int y = 0; y < h; ++y) {
            float t = (float)y / (float)h;
            int r = (int)(0 + t * 18);   // 42 → 60
            int g = (int)(0 + t * 18);
            int b = (int)(0 + t * 18);
            // clip
            if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255;

            HBRUSH br = CreateSolidBrush(RGB(r, g, b));
            RECT line = { 0, y, w, y + 1 };
            FillRect(hdc, &line, br);
            DeleteObject(br);
        }

        // -- subtle border --
        HPEN hBorder = CreatePen(PS_SOLID, 1, RGB(80, 80, 80));
        HPEN hOldPen = (HPEN)SelectObject(hdc, hBorder);
        HBRUSH hNull = (HBRUSH)GetStockObject(NULL_BRUSH);
        HBRUSH hOldBr = (HBRUSH)SelectObject(hdc, hNull);
        RoundRect(hdc, 1, 1, w - 1, h - 1, CORNER_RADIUS, CORNER_RADIUS);
        SelectObject(hdc, hOldPen);
        SelectObject(hdc, hOldBr);
        DeleteObject(hBorder);

        // -- time string --
        time_t now = time(0);
        tm* ltm = localtime(&now);

        WCHAR timeStr[16];
        wsprintfW(timeStr, L"%02d:%02d:%02d",
                  ltm->tm_hour, ltm->tm_min, ltm->tm_sec);

        // -- date string --
        WCHAR dateStr[32];
        wsprintfW(dateStr, L"%04d/%02d/%02d",
                  ltm->tm_year + 1900, ltm->tm_mon + 1, ltm->tm_mday);

        // -- draw time --
        SetBkMode(hdc, TRANSPARENT);
        SelectObject(hdc, hFontTime);
        SetTextColor(hdc, RGB(240, 240, 240));
        RECT rcTime = { 0, 10, w, 55 };
        DrawTextW(hdc, timeStr, -1, &rcTime,
                  DT_CENTER | DT_VCENTER | DT_SINGLELINE);

        // -- draw date --
        // SelectObject(hdc, hFontDate);
        // SetTextColor(hdc, RGB(160, 160, 160));
        // RECT rcDate = { 0, 59, w, 85 };
        // DrawTextW(hdc, dateStr, -1, &rcDate,
        //           DT_CENTER | DT_VCENTER | DT_SINGLELINE);

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

    // ---- cursor ------------------------------------------------
    case WM_SETCURSOR:
        SetCursor(LoadCursor(nullptr, IDC_HAND));
        return TRUE;

    // ---- double-click → lock workstation -----------------------
    case WM_LBUTTONDBLCLK:
        LockWorkStation();
        return 0;

    // ---- drag --------------------------------------------------
    case WM_LBUTTONDOWN:
        isDragging = true;
        SetCapture(hwnd);
        GetCursorPos(&dragStart);
        return 0;

    case WM_MOUSEMOVE: {
        if (isDragging) {
            POINT cur;
            GetCursorPos(&cur);
            RECT wr;
            GetWindowRect(hwnd, &wr);
            int dx = cur.x - dragStart.x;
            int dy = cur.y - dragStart.y;
            SetWindowPos(hwnd, HWND_TOPMOST,
                         wr.left + dx, wr.top + dy,
                         wr.right - wr.left, wr.bottom - wr.top,
                         SWP_NOSIZE);   // re-assert topmost while dragging
            dragStart = cur;
        }
        // re-arm hover tracking each move
        TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT) };
        tme.dwFlags   = TME_HOVER | TME_LEAVE;
        tme.hwndTrack = hwnd;
        tme.dwHoverTime = 100;
        TrackMouseEvent(&tme);
        return 0;
    }

    case WM_LBUTTONUP:
        isDragging = false;
        ReleaseCapture();
        return 0;

    // ---- hover transparency -----------------------------------
    case WM_MOUSEHOVER:
        if (!isMouseOver) {
            isMouseOver = true;
            SetWindowAlpha(hwnd, HOVER_ALPHA);
        }
        return 0;

    case WM_MOUSELEAVE:
        if (isMouseOver) {
            isMouseOver = false;
            SetWindowAlpha(hwnd, BASE_ALPHA);
        }
        return 0;

    // ---- system tray -------------------------------------------
    case WM_TRAYICON:
        if (lParam == WM_RBUTTONUP)
            ShowTrayContextMenu(hwnd);
        else if (lParam == WM_LBUTTONUP) {
            bool vis = IsWindowVisible(hwnd);
            ShowWindow(hwnd, vis ? SW_HIDE : SW_SHOW);
            if (!vis) PinToTop(hwnd);
        }
        return 0;

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
            case 2: {  // toggle show / hide from tray menu
                bool vis = IsWindowVisible(hwnd);
                ShowWindow(hwnd, vis ? SW_HIDE : SW_SHOW);
                if (!vis) PinToTop(hwnd);
                break;
            }
            case 1:  // "Exit"
                DestroyWindow(hwnd);
                break;
        }
        return 0;

    // ---- destroy -----------------------------------------------
    case WM_DESTROY:
        KillTimer(hwnd, TIMER_ID);
        DeleteObject(hFontTime);
        // DeleteObject(hFontDate);
        RemoveTrayIcon(hwnd);
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}

// ===================================================================
//  Helpers
// ===================================================================

void ApplyRoundedCorners(HWND hwnd, int w, int h, int r) {
    HRGN hRgn = CreateRoundRectRgn(0, 0, w, h, r, r);
    SetWindowRgn(hwnd, hRgn, TRUE);
    // hRgn is now owned by the window — do NOT DeleteObject
}

void SetWindowAlpha(HWND hwnd, BYTE alpha) {
    SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), alpha,
                               LWA_COLORKEY | LWA_ALPHA);
}

// Re-insert the window at the top of the topmost Z-order without
// moving/resizing it or stealing keyboard focus.
void PinToTop(HWND hwnd) {
    SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
                 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}

void AddTrayIcon(HWND hwnd) {
    NOTIFYICONDATAW nid = {};
    nid.cbSize           = sizeof(NOTIFYICONDATAW);
    nid.hWnd             = hwnd;
    nid.uID              = TRAY_ID;
    nid.uFlags           = NIF_ICON | NIF_MESSAGE | NIF_TIP;
    nid.uCallbackMessage = WM_TRAYICON;
    nid.hIcon            = CreateClockIcon();
    wcscpy_s(nid.szTip, L"Clock");
    Shell_NotifyIconW(NIM_ADD, &nid);
}

void RemoveTrayIcon(HWND hwnd) {
    NOTIFYICONDATAW nid = {};
    nid.cbSize = sizeof(NOTIFYICONDATAW);
    nid.hWnd   = hwnd;
    nid.uID    = TRAY_ID;
    Shell_NotifyIconW(NIM_DELETE, &nid);
}

void ShowTrayContextMenu(HWND hwnd) {
    POINT pt;
    GetCursorPos(&pt);

    HMENU hMenu = CreatePopupMenu();
    // context-aware label: hide when shown, show when hidden
    AppendMenuW(hMenu, MF_STRING, 2,
                IsWindowVisible(hwnd) ? L"Hide Clock" : L"Show Clock");
    AppendMenuW(hMenu, MF_STRING, 1, L"Exit");

    SetForegroundWindow(hwnd);
    TrackPopupMenu(hMenu, TPM_BOTTOMALIGN | TPM_LEFTALIGN,
                   pt.x, pt.y, 0, hwnd, NULL);
    PostMessage(hwnd, WM_NULL, 0, 0);
    DestroyMenu(hMenu);
}

// ---- custom tray icon: simple clock face ----------------------
HICON CreateClockIcon() {
    const int S = 32;
    HDC hdcScreen = GetDC(NULL);
    HDC hdc = CreateCompatibleDC(hdcScreen);

    // colour bitmap
    HBITMAP hbmColor = CreateCompatibleBitmap(hdcScreen, S, S);
    HBITMAP hbmOld   = (HBITMAP)SelectObject(hdc, hbmColor);

    // clock face background
    {
        HBRUSH br = CreateSolidBrush(RGB(50, 50, 55));
        RECT rc = { 0, 0, S, S };
        FillRect(hdc, &rc, br);
        DeleteObject(br);
    }

    // outer ring
    HPEN penRing  = CreatePen(PS_SOLID, 2, RGB(180, 180, 190));
    HPEN penHand  = CreatePen(PS_SOLID, 2, RGB(230, 230, 240));
    HPEN penOld   = (HPEN)SelectObject(hdc, penRing);
    HBRUSH brFace = CreateSolidBrush(RGB(65, 65, 70));
    HBRUSH brOld  = (HBRUSH)SelectObject(hdc, brFace);
    Ellipse(hdc, 2, 2, S - 2, S - 2);
    DeleteObject(brFace);
    SelectObject(hdc, GetStockObject(NULL_BRUSH));

    // hour ticks — 12, 3, 6, 9
    HPEN penTick = CreatePen(PS_SOLID, 3, RGB(180, 180, 190));
    SelectObject(hdc, penTick);
    // 12 o'clock
    MoveToEx(hdc, S/2, 4, NULL);   LineTo(hdc, S/2, 8);
    // 3 o'clock
    MoveToEx(hdc, S-8, S/2, NULL); LineTo(hdc, S-4, S/2);
    // 6 o'clock
    MoveToEx(hdc, S/2, S-4, NULL); LineTo(hdc, S/2, S-8);
    // 9 o'clock
    MoveToEx(hdc, 4, S/2, NULL);   LineTo(hdc, 8, S/2);
    DeleteObject(penTick);

    // hands — show ~10:10
    SelectObject(hdc, penHand);
    int cx = S / 2, cy = S / 2;
    // hour hand (short, thicker)
    HPEN penHr = CreatePen(PS_SOLID, 3, RGB(230, 230, 240));
    SelectObject(hdc, penHr);
    MoveToEx(hdc, cx, cy, NULL);
    LineTo(hdc, cx - 7, cy - 6);
    DeleteObject(penHr);
    // minute hand (longer)
    HPEN penMn = CreatePen(PS_SOLID, 2, RGB(230, 230, 240));
    SelectObject(hdc, penMn);
    MoveToEx(hdc, cx, cy, NULL);
    LineTo(hdc, cx + 8, cy - 7);
    DeleteObject(penMn);
    // center dot
    SetPixel(hdc, cx, cy, RGB(230, 230, 240));

    // cleanup GDI
    SelectObject(hdc, penOld);
    SelectObject(hdc, brOld);
    DeleteObject(penRing);
    DeleteObject(penHand);

    // mask bitmap — must use PatBlt(WHITENESS) on a monochrome DC;
    // FillRect with a colour brush is unreliable on 1 bpp surfaces.
    HBITMAP hbmMask = CreateBitmap(S, S, 1, 1, NULL);
    {
        HDC hdcMask = CreateCompatibleDC(NULL);
        HBITMAP hbmMaskOld = (HBITMAP)SelectObject(hdcMask, hbmMask);
        PatBlt(hdcMask, 0, 0, S, S, BLACKNESS);          // 0 = opaque in AND mask
        SelectObject(hdcMask, hbmMaskOld);
        DeleteDC(hdcMask);
    }

    SelectObject(hdc, hbmOld);
    DeleteDC(hdc);
    ReleaseDC(NULL, hdcScreen);

    ICONINFO ii = {};
    ii.fIcon    = TRUE;
    ii.hbmColor = hbmColor;
    ii.hbmMask  = hbmMask;
    HICON hIcon = CreateIconIndirect(&ii);

    DeleteObject(hbmColor);
    DeleteObject(hbmMask);

    // fallback if icon creation failed
    if (!hIcon)
        hIcon = LoadIcon(NULL, IDI_APPLICATION);
    return hIcon;
}

// ===================================================================
//  WinMain
// ===================================================================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int) {
    const WCHAR CLASS_NAME[] = L"ClockWindow";

    // ---- single-instance guard (named mutex, session-local) ----
    HANDLE hMutex = CreateMutexW(NULL, TRUE,
                                 L"Local\\ClockAppSingleton_9F2A4C7E1B3D");
    if (hMutex == NULL || GetLastError() == ERROR_ALREADY_EXISTS) {
        // another instance is already running → reveal & focus it
        HWND hExisting = FindWindowW(CLASS_NAME, L"Clock");
        if (hExisting) {
            ShowWindow(hExisting, SW_SHOW);
            SetForegroundWindow(hExisting);
        }
        if (hMutex) CloseHandle(hMutex);
        return 0;
    }

    WNDCLASSW wc = {};
    wc.lpfnWndProc   = WindowProc;
    wc.hInstance     = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.style         = CS_HREDRAW | CS_VREDRAW | CS_DROPSHADOW | CS_DBLCLKS;
    wc.hCursor       = LoadCursor(NULL, IDC_HAND);
    RegisterClassW(&wc);

    // bottom-right corner, 10 px margin
    int x = GetSystemMetrics(SM_CXSCREEN) - WIN_W - 10;
    int y = GetSystemMetrics(SM_CYSCREEN) - WIN_H - 50;

    HWND hwnd = CreateWindowExW(
        WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TOOLWINDOW,
        CLASS_NAME, L"Clock",
        WS_POPUP | WS_VISIBLE,
        x, y, WIN_W, WIN_H,
        nullptr, nullptr, hInstance, nullptr);

    if (!hwnd) return 0;

    // black → fully transparent; base alpha set separately
    SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0),
                               BASE_ALPHA, LWA_COLORKEY | LWA_ALPHA);

    MSG msg;
    while (GetMessageW(&msg, nullptr, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }
    return 0;
}
