import ctypes
import ctypes.wintypes
import time
import threading
from ws_package.log.writer import LogWriter

logger = LogWriter.file(__name__)

class WindowsMouseDetector:
    """Windows-specific mouse detection using Win32 API as fallback"""
    
    def __init__(self):
        self.is_running = False
        self.last_position = (0, 0)
        self.callback = None
        self.detection_thread = None
        
        # Win32 API structures
        self.POINT = ctypes.wintypes.POINT
        self.HWND = ctypes.wintypes.HWND
        self.LPARAM = ctypes.wintypes.LPARAM
        self.WPARAM = ctypes.wintypes.WPARAM
        self.DWORD = ctypes.wintypes.DWORD
        self.BOOL = ctypes.wintypes.BOOL
        
        # Load Windows DLLs
        self.user32 = ctypes.windll.user32
        self.kernel32 = ctypes.windll.kernel32

        # Ensure per-monitor DPI awareness so GetCursorPos returns physical
        # pixel coordinates on high-DPI displays (required on Win11).
        # SetProcessDpiAwareness returns E_ACCESSDENIED if already set — safe to ignore.
        try:
            ctypes.windll.shcore.SetProcessDpiAwareness(2)  # PROCESS_PER_MONITOR_DPI_AWARE
            logger.debug("DPI awareness set to PROCESS_PER_MONITOR_DPI_AWARE")
        except Exception:
            try:
                self.user32.SetProcessDPIAware()  # Win Vista–8 fallback
                logger.debug("DPI awareness set via legacy SetProcessDPIAware")
            except Exception:
                logger.warning("Could not set DPI awareness; cursor coordinates may be incorrect on high-DPI displays")
        
    def get_cursor_position(self):
        """Get current cursor position using Win32 API"""
        try:
            point = self.POINT()
            self.user32.GetCursorPos(ctypes.byref(point))
            return (point.x, point.y)
        except Exception as e:
            logger.error("Error getting cursor position: %s", e)
            return (0, 0)
    
    def get_last_input_time(self):
        """Get last input time using Win32 API"""
        try:
            class LASTINPUTINFO(ctypes.Structure):
                _fields_ = [("cbSize", ctypes.c_uint), ("dwTime", ctypes.c_int)]
            
            lii = LASTINPUTINFO()
            lii.cbSize = ctypes.sizeof(LASTINPUTINFO)
            self.user32.GetLastInputInfo(ctypes.byref(lii))
            return lii.dwTime
        except Exception as e:
            logger.error("Error getting last input time: %s", e)
            return 0
    
    def is_system_idle(self, threshold_seconds=60):
        """Check if system is idle using Win32 API"""
        try:
            last_input = self.get_last_input_time()
            current_time = self.kernel32.GetTickCount()
            idle_time = (current_time - last_input) / 1000.0
            return idle_time > threshold_seconds
        except Exception as e:
            logger.error("Error checking system idle: %s", e)
            return False
    
    def start_polling_detection(self, callback, interval=0.1):
        """Start polling-based mouse detection as fallback"""
        if self.is_running:
            logger.warning("Mouse detection already running")
            return
            
        self.callback = callback
        self.is_running = True
        
        def poll_loop():
            last_pos = self.get_cursor_position()
            last_activity_time = time.time()
            
            while self.is_running:
                try:
                    current_pos = self.get_cursor_position()
                    current_time = time.time()
                    
                    # Check if mouse moved
                    if current_pos != last_pos:
                        if self.callback:
                            self.callback('move', current_pos[0], current_pos[1])
                        last_activity_time = current_time
                        last_pos = current_pos
                    
                    # Check for system wake
                    if current_time - last_activity_time > 5:  # Check every 5 seconds
                        if not self.is_system_idle(10):  # System became active
                            if self.callback:
                                self.callback('wake', 0, 0)
                        last_activity_time = current_time
                    
                    time.sleep(interval)
                    
                except Exception as e:
                    logger.error("Error in polling detection: %s", e)
                    time.sleep(1)
        
        self.detection_thread = threading.Thread(target=poll_loop, daemon=True, name="WindowsMouseDetector")
        self.detection_thread.start()
        logger.info("Windows mouse polling detection started")
    
    def stop_polling_detection(self):
        """Stop polling-based detection"""
        self.is_running = False
        if self.detection_thread and self.detection_thread.is_alive():
            self.detection_thread.join(timeout=5)
        logger.info("Windows mouse polling detection stopped")
    
    def check_permissions(self):
        """Check if application has necessary permissions"""
        try:
            # Check if we can get cursor position
            pos = self.get_cursor_position()
            if pos != (0, 0):
                logger.info("Windows mouse detection permissions OK")
                return True
            else:
                logger.warning("Windows mouse detection permissions may be limited")
                return False
        except Exception as e:
            logger.error("Windows mouse detection permission check failed: %s", e)
            return False

class WindowsMouseFallback:
    """Fallback mouse detection when pynput fails"""
    
    def __init__(self, device_listener):
        self.device_listener = device_listener
        self.windows_detector = WindowsMouseDetector()
        self.fallback_active = False
        
    def start_fallback_detection(self):
        """Start fallback detection if pynput fails"""
        if self.windows_detector.check_permissions():
            self.windows_detector.start_polling_detection(self.handle_fallback_event)
            self.fallback_active = True
            logger.info("Windows fallback mouse detection activated")
            return True
        else:
            logger.error("Windows fallback mouse detection failed - insufficient permissions")
            return False
    
    def stop_fallback_detection(self):
        """Stop fallback detection"""
        if self.fallback_active:
            self.windows_detector.stop_polling_detection()
            self.fallback_active = False
            logger.info("Windows fallback mouse detection stopped")
    
    def handle_fallback_event(self, event_type, x, y):
        """Handle events from fallback detection"""
        try:
            current_time = time.time()
            second_index = int(current_time % self.device_listener.size) % self.device_listener.array_size
            
            if event_type == 'move':
                # Simulate mouse movement
                self.device_listener.last_mouse_activity = current_time
                self.device_listener.mouse_move_count += 1
                self.device_listener.mouse_activity_array[second_index] = True
                
                # Update position
                self.device_listener.prev_mouse_x, self.device_listener.prev_mouse_y = x, y
                
            elif event_type == 'wake':
                # System woke from sleep
                logger.info("System wake detected via fallback detection")
                self.device_listener.last_mouse_activity = current_time
                
        except Exception as e:
            logger.error("Error handling fallback event: %s", e) 
