import ctypes
from ctypes import wintypes
import logging

# Win32 Types & Constants
# Use standard wintypes to ensure architecture safety (32-bit vs 64-bit)
WPARAM = wintypes.WPARAM
LPARAM = wintypes.LPARAM
HWND = wintypes.HWND
UINT = wintypes.UINT

# LRESULT is technically LONG_PTR (signed)
# In 64-bit, c_longlong. In 32-bit, c_long.
if ctypes.sizeof(ctypes.c_void_p) == 8:
    LRESULT = ctypes.c_longlong
else:
    LRESULT = ctypes.c_long

# Callback Type
WNDPROC = ctypes.WINFUNCTYPE(LRESULT, HWND, UINT, WPARAM, LPARAM)

GWLP_WNDPROC = -4
WM_NCHITTEST = 0x0084
HTTRANSPARENT = -1
HTCLIENT = 1
HTCAPTION = 2

user32 = ctypes.windll.user32

# SetWindowLongPtr Check
if hasattr(user32, "SetWindowLongPtrW"):
    SetWindowLongPtr = user32.SetWindowLongPtrW
    SetWindowLongPtr.argtypes = [HWND, ctypes.c_int, ctypes.c_void_p]
    SetWindowLongPtr.restype = ctypes.c_void_p
else:
    SetWindowLongPtr = user32.SetWindowLongW
    SetWindowLongPtr.argtypes = [HWND, ctypes.c_int, ctypes.c_long]
    SetWindowLongPtr.restype = ctypes.c_long

# Define ArgTypes to prevent truncation/stack corruption
user32.CallWindowProcW.argtypes = [ctypes.c_void_p, HWND, UINT, WPARAM, LPARAM]
user32.CallWindowProcW.restype = LRESULT

user32.ScreenToClient.argtypes = [HWND, ctypes.POINTER(wintypes.POINT)]
user32.ScreenToClient.restype = ctypes.c_bool

# DPI API
try:
    GetDpiForWindow = user32.GetDpiForWindow
    GetDpiForWindow.argtypes = [HWND]
    GetDpiForWindow.restype = UINT
except AttributeError:
    GetDpiForWindow = None

def LOWORD(l): return l & 0xffff
def HIWORD(l): return (l >> 16) & 0xffff

class WindowHook:
    def __init__(self, hwnd, width, height, initial_scale=1.0):
        self.hwnd = hwnd
        self.old_wnd_proc = None
        self.new_wnd_proc = WNDPROC(self.wnd_proc_callback) 
        
        # TIGHT FIT: [20, 20, 400, 120] 
        # (Window 420x140, Pill 380x100)
        self.logical_rect = [20, 20, 20+380, 20+100]
        self.current_scale = initial_scale
        self.enabled = True # New flag

    def set_enabled(self, enabled):
        self.enabled = enabled

    def install(self):
        proc_address = ctypes.cast(self.new_wnd_proc, ctypes.c_void_p)
        self.old_wnd_proc = SetWindowLongPtr(self.hwnd, GWLP_WNDPROC, proc_address)

    def wnd_proc_callback(self, hwnd, msg, wParam, lParam):
        try:
            if msg == WM_NCHITTEST:
                # If disabled (invisible/inactive), let clicks pass through (HTTRANSPARENT)
                if not self.enabled:
                    return HTTRANSPARENT
                    
                res = self.on_nchittest(lParam)
                if res != 0: 
                    return res
        
            return user32.CallWindowProcW(self.old_wnd_proc, hwnd, msg, wParam, lParam)
        except Exception:
            # Swallow exceptions in callback to prevent recursive crash loops
            return 0

    def on_nchittest(self, lParam):
        if GetDpiForWindow:
            dpi = GetDpiForWindow(self.hwnd)
            if dpi > 0:
                self.current_scale = dpi / 96.0
        
        # Physics
        s = self.current_scale
        phys_rect = [
            self.logical_rect[0] * s,
            self.logical_rect[1] * s,
            self.logical_rect[2] * s, 
            self.logical_rect[3] * s
        ]

        x_screen = LOWORD(lParam)
        if x_screen > 32767: x_screen -= 65536
        y_screen = HIWORD(lParam)
        if y_screen > 32767: y_screen -= 65536
        
        pt = wintypes.POINT(x_screen, y_screen)
        user32.ScreenToClient(self.hwnd, ctypes.byref(pt))
        lx = pt.x
        ly = pt.y
        
        inside = (lx >= phys_rect[0] and lx <= phys_rect[2] and
                  ly >= phys_rect[1] and ly <= phys_rect[3])
                  
        if inside:
            return HTCAPTION 
        else:
            return HTTRANSPARENT
