import ctypes
import ctypes.wintypes as wintypes
import threading

import win32con
import hid
from threading import Thread

# Define some constants from the Windows API
WM_DEVICECHANGE = 0x0219
DBT_DEVICEARRIVAL = 0x8000
DBT_DEVICEREMOVECOMPLETE = 0x8004
#GUID_DEVINTERFACE_USB_DEVICE = "{A5DCBF10-6530-11D2-901F-00C04FB951ED}"
GUID_DEVINTERFACE_USB_DEVICE = "{a5dcbf10-6530-11d2-901f-00c04fb951ed}"
WNDPROCTYPE = ctypes.WINFUNCTYPE(ctypes.c_int, wintypes.HWND, ctypes.c_uint, wintypes.WPARAM, wintypes.LPARAM)


class DeviceMonitor:
    def __init__(self):
        self.on_new_device = None
        self.previous_devices = []

    def refresh_hid_tree(self):
        # diff the current list with the new list
        new_devs = hid.enumerate()
        for new_dev in new_devs:
            if new_dev not in self.previous_devices:
                # New device found
                if self.on_new_device:
                    self.on_new_device(new_dev)
        self.previous_devices = new_devs

    def register_on_new_hid_device(self, callback):
        self.on_new_device = callback


def on_new_headset(dev):
    if dev['vendor_id'] == 0x35bd and dev['product_id'] == 0x0101:
        print("Found device: {}".format(dev['serial_number']))
        device = hid.Device(vid=0x35bd, pid=0x0101, serial=dev['serial_number'])
        device.send_feature_report(bytes([0, ord('L'), 255, 0, 0]))


instance = DeviceMonitor()
instance.refresh_hid_tree()

# Define the callback function that will handle incoming messages
def wnd_proc(hwnd: wintypes.HWND, msg: wintypes.UINT, wParam: wintypes.WPARAM, lParam: wintypes.LPARAM) -> int:
    if msg == WM_DEVICECHANGE:
        if wParam == DBT_DEVICEARRIVAL:
            instance.refresh_hid_tree()
    return 0

def create_bg_window():
    # Create a window class and register it
    class WNDCLASS(ctypes.Structure):
        _fields_ = [("style", wintypes.UINT),
                    ("lpfnWndProc", WNDPROCTYPE),
                    ("cbClsExtra", ctypes.c_int),
                    ("cbWndExtra", ctypes.c_int),
                    ("hInstance", wintypes.HANDLE),
                    ("hIcon", wintypes.HANDLE),
                    ("hCursor", wintypes.HANDLE),
                    ("hbrBackground", wintypes.HANDLE),
                    ("lpszMenuName", ctypes.c_wchar_p),
                    ("lpszClassName", ctypes.c_wchar_p)]
    wnd_class = WNDCLASS()
    wnd_class.lpfnWndProc = WNDPROCTYPE(wnd_proc)
    wnd_class.hInstance = ctypes.windll.kernel32.GetModuleHandleW(None)
    wnd_class.lpszClassName = "MyWndClass"
    wnd_class_atom = ctypes.windll.user32.RegisterClassW(ctypes.byref(wnd_class))

    # Create a window and get its handle
    hwnd = ctypes.windll.user32.CreateWindowExW(0, "MyWndClass", None, 0, 0, 0, 0, 0, wintypes.HWND(0), wintypes.HANDLE(0), ctypes.c_long(wnd_class.hInstance), None)
    #
    # Register for device change notifications
    class DEV_BROADCAST_DEVICEINTERFACE(ctypes.Structure):
        _fields_ = [("dbcc_size", ctypes.c_ulong),
                    ("dbcc_devicetype", ctypes.c_ulong),
                    ("dbcc_reserved", ctypes.c_ulong),
                    ("dbcc_classguid", ctypes.c_byte * 16),
                    ("dbcc_name", ctypes.c_wchar)]
    dev_notify_filter = DEV_BROADCAST_DEVICEINTERFACE()
    dev_notify_filter.dbcc_size = ctypes.sizeof(dev_notify_filter)
    dev_notify_filter.dbcc_devicetype = win32con.DBT_DEVTYP_DEVICEINTERFACE
    guid_bytes = bytes.fromhex(GUID_DEVINTERFACE_USB_DEVICE[1:-1].replace('-', ''))
    for i in range(len(guid_bytes)):
        dev_notify_filter.dbcc_classguid[i] = guid_bytes[i]
    hdev_notify = ctypes.windll.user32.RegisterDeviceNotificationW(hwnd, ctypes.byref(dev_notify_filter), 0x00000004)
    #
    # Enter the message loop
    msg = wintypes.MSG()
    while ctypes.windll.user32.GetMessageW(ctypes.byref(msg), None, 0, 0):
        ctypes.windll.user32.TranslateMessage(ctypes.byref(msg))
        ctypes.windll.user32.DispatchMessageW(ctypes.byref(msg))

bg_win_thread = threading.Thread(target=create_bg_window).start()

#
# # Unregister device change notifications
# windll.user32.UnregisterDeviceNotification(hdev_notify)
