"""
wivexx_eyedropper.py — screen colour picker  (v1.0)
═══════════════════════════════════════════════════════════════════════════════
Grab the colour of any pixel on screen and copy its hex.

Two ways to trigger it:
  • INSTANT (hotkey): hover the cursor over a colour and press the eyedropper
    shortcut → pick_under_cursor() samples that pixel immediately (no stray click).
  • ARMED (one-shot click): arm_pick() waits for your NEXT mouse click anywhere and
    samples the pixel there — handy when invoked from the UI / command palette.

Sampling uses Win32 GetPixel on the screen DC (works over any app, incl. games in
windowed/borderless mode). Hex is copied to the Windows clipboard via clip.exe.
"""
from __future__ import annotations

import ctypes
import subprocess
import threading
import time
from ctypes import wintypes
from typing import Callable, Optional

_CLR_INVALID = 0xFFFFFFFF
_CREATE_NO_WINDOW = 0x08000000

_setup_done = False


def _setup() -> bool:
    """64-bit-safe prototypes for the GDI/user32 calls (so HDC/HWND don't truncate)."""
    global _setup_done
    if _setup_done:
        return True
    try:
        u = ctypes.windll.user32
        g = ctypes.windll.gdi32
        u.GetDC.restype = wintypes.HDC
        u.GetDC.argtypes = [wintypes.HWND]
        u.ReleaseDC.restype = ctypes.c_int
        u.ReleaseDC.argtypes = [wintypes.HWND, wintypes.HDC]
        u.GetCursorPos.argtypes = [ctypes.POINTER(wintypes.POINT)]
        g.GetPixel.restype = wintypes.COLORREF
        g.GetPixel.argtypes = [wintypes.HDC, ctypes.c_int, ctypes.c_int]
        _setup_done = True
        return True
    except Exception:
        return False


def _hex_at(x: int, y: int) -> Optional[str]:
    if not _setup():
        return None
    u = ctypes.windll.user32
    g = ctypes.windll.gdi32
    hdc = u.GetDC(0)                                   # 0 = the whole screen
    if not hdc:
        return None
    try:
        col = int(g.GetPixel(hdc, int(x), int(y)))
    finally:
        u.ReleaseDC(0, hdc)
    if col == _CLR_INVALID or col < 0:
        return None
    r = col & 0xFF
    gg = (col >> 8) & 0xFF
    b = (col >> 16) & 0xFF                             # COLORREF is 0x00BBGGRR
    return "#%02X%02X%02X" % (r, gg, b)


def hex_under_cursor() -> Optional[str]:
    """Hex colour of the pixel currently under the mouse cursor (or None)."""
    if not _setup():
        return None
    pt = wintypes.POINT()
    if not ctypes.windll.user32.GetCursorPos(ctypes.byref(pt)):
        return None
    return _hex_at(pt.x, pt.y)


def copy_to_clipboard(text: str) -> bool:
    try:
        subprocess.run(["clip"], input=str(text), text=True,
                       creationflags=_CREATE_NO_WINDOW)
        return True
    except Exception:
        return False


def pick_under_cursor(notify: Optional[Callable[[str], None]] = None) -> Optional[str]:
    """INSTANT pick: sample under the cursor, copy the hex, notify. Returns the hex."""
    hexv = hex_under_cursor()
    if not hexv:
        if notify:
            notify("")
        return None
    copy_to_clipboard(hexv)
    if notify:
        notify(hexv)
    return hexv


_arm_lock = threading.Lock()
_arming = False


def arm_pick(notify: Optional[Callable[[str], None]] = None, timeout: float = 20.0) -> bool:
    """ARMED pick: wait for the next mouse click anywhere, sample the pixel there,
    copy the hex, notify. Returns False if it couldn't arm (pynput missing / busy)."""
    global _arming
    with _arm_lock:
        if _arming:
            return False
        _arming = True
    try:
        from pynput import mouse as _ms
    except Exception:
        _arming = False
        return False

    state = {"done": False}

    def _on_click(x, y, button, pressed):
        if not pressed:
            return
        state["done"] = True
        hexv = _hex_at(x, y) or hex_under_cursor()
        if hexv:
            copy_to_clipboard(hexv)
        if notify:
            try:
                notify(hexv or "")
            except Exception:
                pass
        return False                                  # stop the listener

    def _run():
        global _arming
        try:
            lis = _ms.Listener(on_click=_on_click)
            lis.start()
            t0 = time.time()
            while lis.running and (time.time() - t0) < timeout:
                time.sleep(0.05)
            if lis.running:
                lis.stop()
        except Exception:
            pass
        finally:
            _arming = False

    threading.Thread(target=_run, daemon=True, name="wivexx-eyedropper").start()
    return True


if __name__ == "__main__":
    print("colour under cursor:", hex_under_cursor())
