import time
import ctypes
from ctypes import wintypes
import os
import sys

log_fp = open(r"c:\Projects\AutoPOE2\scratch\escape_test.log", "w", encoding="utf-8")
old_print = print
def print(*args, **kwargs):
    old_print(*args, **kwargs)
    old_print(*args, **kwargs, file=log_fp)
    log_fp.flush()

user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32

ULONG_PTR = ctypes.c_uint64

class MOUSEINPUT(ctypes.Structure):
    _fields_ = [
        ('dx', wintypes.LONG),
        ('dy', wintypes.LONG),
        ('mouseData', wintypes.DWORD),
        ('dwFlags', wintypes.DWORD),
        ('time', wintypes.DWORD),
        ('dwExtraInfo', ULONG_PTR),
    ]

class KEYBDINPUT(ctypes.Structure):
    _fields_ = [
        ('wVk', wintypes.WORD),
        ('wScan', wintypes.WORD),
        ('dwFlags', wintypes.DWORD),
        ('time', wintypes.DWORD),
        ('dwExtraInfo', ULONG_PTR),
    ]

class HARDWAREINPUT(ctypes.Structure):
    _fields_ = [
        ('uMsg', wintypes.DWORD),
        ('wParamL', wintypes.WORD),
        ('wParamH', wintypes.WORD),
    ]

class INPUT_UNION(ctypes.Union):
    _fields_ = [
        ('mi', MOUSEINPUT),
        ('ki', KEYBDINPUT),
        ('hi', HARDWAREINPUT),
    ]

class INPUT(ctypes.Structure):
    _anonymous_ = ('u',)
    _fields_ = [
        ('type', wintypes.DWORD),
        ('u', INPUT_UNION),
    ]

KEYEVENTF_SCANCODE = 0x0008
KEYEVENTF_KEYUP = 0x0002
INPUT_KEYBOARD = 1
INPUT_MOUSE = 0

def send_scancode_down(scan):
    inp = INPUT(type=INPUT_KEYBOARD)
    inp.ki.wVk = 0
    inp.ki.wScan = scan
    inp.ki.dwFlags = KEYEVENTF_SCANCODE
    return user32.SendInput(1, ctypes.byref(inp), ctypes.sizeof(INPUT))

def send_scancode_up(scan):
    inp = INPUT(type=INPUT_KEYBOARD)
    inp.ki.wVk = 0
    inp.ki.wScan = scan
    inp.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP
    return user32.SendInput(1, ctypes.byref(inp), ctypes.sizeof(INPUT))

def main():
    try:
        hwinsta = user32.OpenWindowStationW("WinSta0", False, 0x10000000)
        if hwinsta: user32.SetProcessWindowStation(hwinsta)
        hdesk = user32.OpenDesktopW("Default", 0, False, 0x10000000)
        if hdesk: user32.SetThreadDesktop(hdesk)
    except Exception as e:
        print(f"Desktop error: {e}")

    hwnd = user32.FindWindowW("POEWindowClass", None)
    if not hwnd:
        hwnd = user32.FindWindowW(None, "Path of Exile 2")
    print(f"HWND: {hwnd}")
    if not hwnd:
        return

    # Focus
    user32.ShowWindow(hwnd, 9)
    user32.SetForegroundWindow(hwnd)
    user32.BringWindowToTop(hwnd)
    time.sleep(0.3)

    # Read XYZ
    pid = 43084
    h_proc = kernel32.OpenProcess(0x0010, False, pid)
    xyz_addr = 0x2AF791A0550

    def read_xyz():
        if not h_proc: return (0.0, 0.0, 0.0)
        buf = ctypes.create_string_buffer(12)
        bytes_read = ctypes.c_size_t(0)
        kernel32.ReadProcessMemory(h_proc, ctypes.c_void_p(xyz_addr), buf, 12, ctypes.byref(bytes_read))
        if bytes_read.value == 12:
            import struct
            return struct.unpack('3f', buf.raw)
        return (0.0, 0.0, 0.0)

    print(f"Initial XYZ: {read_xyz()}")

    # 1. Test 'D' (Scan 0x20) towards the open path on the right for 1.5 seconds
    xyz_before = read_xyz()
    print("Holding 'D' (Scan 0x20) for 1.5s towards the path...")
    send_scancode_down(0x20)
    time.sleep(1.5)
    send_scancode_up(0x20)
    time.sleep(0.4)
    xyz_after = read_xyz()
    print(f"D: Before {xyz_before} -> After {xyz_after}")

    # 2. Test Dodge Roll (Space scan 0x39) while holding 'D' (0x20)
    xyz_before = read_xyz()
    print("Holding 'D' + tapping Space (Dodge Roll)...")
    send_scancode_down(0x20)
    time.sleep(0.1)
    send_scancode_down(0x39)
    time.sleep(0.08)
    send_scancode_up(0x39)
    time.sleep(0.5)
    send_scancode_up(0x20)
    time.sleep(0.5)
    xyz_after = read_xyz()
    print(f"D + Space: Before {xyz_before} -> After {xyz_after}")

    # 3. Test left click on the open dirt path at (1600, 600)
    xyz_before = read_xyz()
    print("Clicking path at (1600, 600)...")
    user32.SetCursorPos(1600, 600)
    time.sleep(0.05)
    user32.mouse_event(0x0002, 0, 0, 0, 0) # LEFTDOWN
    time.sleep(0.08)
    user32.mouse_event(0x0004, 0, 0, 0, 0) # LEFTUP
    time.sleep(1.0)
    xyz_after = read_xyz()
    print(f"Click (1600, 600): Before {xyz_before} -> After {xyz_after}")

    # Capture result
    sys.path.insert(0, r"C:\Projects\AutoPOE2")
    from tools.autonomous_map_runner import ScreenCapturerGDI
    c = ScreenCapturerGDI()
    c.capture(r"C:\Projects\AutoPOE2\captures\test_escape_after.png")

    if h_proc: kernel32.CloseHandle(h_proc)
    print("Done.")

if __name__ == "__main__":
    main()
