"""
wivexx_clipboard.py — read/write the Windows clipboard  (v1.15)
═══════════════════════════════════════════════════════════════════════
Tiny ctypes wrapper around the Win32 clipboard (CF_UNICODETEXT) — no extra
dependencies.  Used by the clipboard commands ("summarize what I copied",
"translate my clipboard to Spanish") so WIVEXX can read what you've copied,
process it with Claude, and paste the result straight back.

Public API:
    get_text() -> Optional[str]
    set_text(s: str) -> bool
"""

import ctypes
import time
from ctypes import wintypes
from typing import Optional

_CF_UNICODETEXT = 13
_GMEM_MOVEABLE = 0x0002

try:
    _user32 = ctypes.windll.user32
    _kernel32 = ctypes.windll.kernel32

    _user32.OpenClipboard.argtypes = [wintypes.HWND]
    _user32.OpenClipboard.restype = wintypes.BOOL
    _user32.GetClipboardData.argtypes = [wintypes.UINT]
    _user32.GetClipboardData.restype = wintypes.HANDLE
    _user32.SetClipboardData.argtypes = [wintypes.UINT, wintypes.HANDLE]
    _user32.SetClipboardData.restype = wintypes.HANDLE
    _user32.EmptyClipboard.restype = wintypes.BOOL
    _user32.CloseClipboard.restype = wintypes.BOOL

    _kernel32.GlobalLock.argtypes = [wintypes.HGLOBAL]
    _kernel32.GlobalLock.restype = ctypes.c_void_p
    _kernel32.GlobalUnlock.argtypes = [wintypes.HGLOBAL]
    _kernel32.GlobalAlloc.argtypes = [wintypes.UINT, ctypes.c_size_t]
    _kernel32.GlobalAlloc.restype = wintypes.HGLOBAL
    _AVAILABLE = True
except Exception:                                    # pragma: no cover
    _AVAILABLE = False


def _open(retries: int = 6) -> bool:
    for _ in range(retries):
        if _user32.OpenClipboard(None):
            return True
        time.sleep(0.05)
    return False


def get_text() -> Optional[str]:
    """Current clipboard text, or None if empty / unavailable."""
    if not _AVAILABLE or not _open():
        return None
    try:
        handle = _user32.GetClipboardData(_CF_UNICODETEXT)
        if not handle:
            return None
        ptr = _kernel32.GlobalLock(handle)
        if not ptr:
            return None
        try:
            return ctypes.c_wchar_p(ptr).value
        finally:
            _kernel32.GlobalUnlock(handle)
    except Exception:
        return None
    finally:
        _user32.CloseClipboard()


def set_text(s: str) -> bool:
    """Replace the clipboard contents with *s*.  Returns True on success."""
    if not _AVAILABLE or s is None or not _open():
        return False
    try:
        _user32.EmptyClipboard()
        data = ctypes.create_unicode_buffer(s)
        size = ctypes.sizeof(data)
        h_global = _kernel32.GlobalAlloc(_GMEM_MOVEABLE, size)
        if not h_global:
            return False
        dst = _kernel32.GlobalLock(h_global)
        if not dst:
            return False
        ctypes.memmove(dst, ctypes.addressof(data), size)
        _kernel32.GlobalUnlock(h_global)
        # ownership of h_global passes to the system on success
        if not _user32.SetClipboardData(_CF_UNICODETEXT, h_global):
            return False
        return True
    except Exception:
        return False
    finally:
        _user32.CloseClipboard()
