"""
Pygame-based Display HAL for desktop testing.

This HAL implementation uses Pygame to provide a desktop window
for testing the e-reader application without physical hardware.

Features:
- Window display with PIL image rendering
- Mouse input converted to touch events
- Keyboard shortcuts for common actions
- Gesture detection (swipes via mouse drag)

Usage:
    from dreader.hal_pygame import PygameDisplayHAL
    from dreader.main import DReaderApplication, AppConfig

    hal = PygameDisplayHAL(width=800, height=1200)
    config = AppConfig(display_hal=hal, library_path="~/Books")
    app = DReaderApplication(config)

    await hal.run_event_loop(app)
"""

import asyncio
import logging
from typing import Optional
from PIL import Image
import numpy as np

from .hal import EventLoopHAL
from .gesture import TouchEvent, GestureType

logger = logging.getLogger(__name__)

# Pygame is optional - only needed for desktop testing
try:
    import pygame
    PYGAME_AVAILABLE = True
except ImportError:
    PYGAME_AVAILABLE = False
    logger.warning("Pygame not available. Install with: pip install pygame")


class PygameDisplayHAL(EventLoopHAL):
    """
    Pygame-based display HAL for desktop testing.

    This implementation provides a desktop window that simulates
    an e-reader display with touch input.
    """

    def __init__(
        self,
        width: int = 800,
        height: int = 1200,
        title: str = "DReader E-Book Reader",
        fullscreen: bool = False
    ):
        """
        Initialize Pygame display.

        Args:
            width: Window width in pixels
            height: Window height in pixels
            title: Window title
            fullscreen: If True, open in fullscreen mode
        """
        if not PYGAME_AVAILABLE:
            raise RuntimeError("Pygame is required for PygameDisplayHAL. Install with: pip install pygame")

        self.width = width
        self.height = height
        self.title = title
        self.fullscreen = fullscreen

        self.screen = None
        self.running = False

        # Touch/gesture tracking
        self.mouse_down_pos: Optional[tuple[int, int]] = None
        self.mouse_down_time: float = 0
        self.drag_threshold = 20  # pixels (reduced from 30 for easier swiping)
        self.long_press_duration = 0.5  # seconds

        logger.info(f"PygameDisplayHAL initialized: {width}x{height}")

    async def initialize(self):
        """Initialize Pygame and create window."""
        logger.info("Initializing Pygame")
        pygame.init()

        # Set up display
        flags = pygame.DOUBLEBUF
        if self.fullscreen:
            flags |= pygame.FULLSCREEN

        self.screen = pygame.display.set_mode((self.width, self.height), flags)
        pygame.display.set_caption(self.title)

        # Set up font for messages
        pygame.font.init()

        logger.info("Pygame initialized successfully")

    async def cleanup(self):
        """Clean up Pygame resources."""
        logger.info("Cleaning up Pygame")
        if pygame.get_init():
            pygame.quit()

    async def show_image(self, image: Image.Image):
        """
        Display PIL image on Pygame window.

        Args:
            image: PIL Image to display
        """
        if not self.screen:
            logger.warning("Screen not initialized")
            return

        # Convert PIL image to pygame surface
        # PIL uses RGB, pygame uses RGB
        if image.mode != 'RGB':
            image = image.convert('RGB')

        # Resize if needed
        if image.size != (self.width, self.height):
            image = image.resize((self.width, self.height), Image.Resampling.LANCZOS)

        # Convert to numpy array, then to pygame surface
        img_array = np.array(image)
        surface = pygame.surfarray.make_surface(np.transpose(img_array, (1, 0, 2)))

        # Blit to screen
        self.screen.blit(surface, (0, 0))
        pygame.display.flip()

        # Small delay to prevent excessive CPU usage
        await asyncio.sleep(0.001)

    async def get_touch_event(self) -> Optional[TouchEvent]:
        """
        Process pygame events and convert to TouchEvent.

        Returns:
            TouchEvent if available, None otherwise
        """
        if not pygame.get_init():
            return None

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                logger.info("Quit event received")
                self.running = False
                return None

            elif event.type == pygame.MOUSEBUTTONDOWN:
                # Mouse down - start tracking for gesture
                self.mouse_down_pos = event.pos
                self.mouse_down_time = pygame.time.get_ticks() / 1000.0
                logger.info(f"[MOUSE] Button DOWN at {event.pos}")

            elif event.type == pygame.MOUSEMOTION:
                # Show drag indicator while mouse is down
                if self.mouse_down_pos and pygame.mouse.get_pressed()[0]:
                    current_pos = event.pos
                    dx = current_pos[0] - self.mouse_down_pos[0]
                    dy = current_pos[1] - self.mouse_down_pos[1]
                    distance = (dx**2 + dy**2) ** 0.5

                    # Log dragging in progress
                    if distance > 5:  # Log any significant drag
                        logger.info(f"[DRAG] Moving: dx={dx:.0f}, dy={dy:.0f}, distance={distance:.0f}px")

                    # Only show if dragging beyond threshold
                    if distance > self.drag_threshold:
                        # Draw a line showing the swipe direction
                        if self.screen:
                            # This is just for visual feedback during drag
                            # The actual gesture detection happens on mouse up
                            pass

            elif event.type == pygame.MOUSEBUTTONUP:
                if self.mouse_down_pos is None:
                    logger.warning("[MOUSE] Button UP but no down position recorded")
                    continue

                mouse_up_pos = event.pos
                mouse_up_time = pygame.time.get_ticks() / 1000.0

                # Calculate distance and time
                dx = mouse_up_pos[0] - self.mouse_down_pos[0]
                dy = mouse_up_pos[1] - self.mouse_down_pos[1]
                distance = (dx**2 + dy**2) ** 0.5
                duration = mouse_up_time - self.mouse_down_time

                logger.info(f"[MOUSE] Button UP at {mouse_up_pos}")
                logger.info(f"[GESTURE] dx={dx:.0f}, dy={dy:.0f}, distance={distance:.0f}px, duration={duration:.2f}s, threshold={self.drag_threshold}px")

                # Detect gesture type
                gesture = None
                x, y = mouse_up_pos

                if distance < self.drag_threshold:
                    # Tap or long press
                    if duration >= self.long_press_duration:
                        gesture = GestureType.LONG_PRESS
                        logger.info(f"[GESTURE] ✓ Detected: LONG_PRESS")
                    else:
                        gesture = GestureType.TAP
                        logger.info(f"[GESTURE] ✓ Detected: TAP")
                else:
                    # Swipe
                    if abs(dx) > abs(dy):
                        # Horizontal swipe
                        if dx > 0:
                            gesture = GestureType.SWIPE_RIGHT
                            logger.info(f"[GESTURE] ✓ Detected: SWIPE_RIGHT (dx={dx:.0f})")
                        else:
                            gesture = GestureType.SWIPE_LEFT
                            logger.info(f"[GESTURE] ✓ Detected: SWIPE_LEFT (dx={dx:.0f})")
                    else:
                        # Vertical swipe
                        if dy > 0:
                            gesture = GestureType.SWIPE_DOWN
                            logger.info(f"[GESTURE] ✓ Detected: SWIPE_DOWN (dy={dy:.0f})")
                        else:
                            gesture = GestureType.SWIPE_UP
                            logger.info(f"[GESTURE] ✓ Detected: SWIPE_UP (dy={dy:.0f})")

                # Reset tracking
                self.mouse_down_pos = None

                if gesture:
                    logger.info(f"[EVENT] Returning TouchEvent: {gesture.value} at ({x}, {y})")
                    return TouchEvent(gesture, x, y)
                else:
                    logger.warning("[EVENT] No gesture detected (should not happen)")

            elif event.type == pygame.KEYDOWN:
                # Keyboard shortcuts
                return await self._handle_keyboard(event)

        return None

    async def _handle_keyboard(self, event) -> Optional[TouchEvent]:
        """
        Handle keyboard shortcuts.

        Args:
            event: Pygame keyboard event

        Returns:
            TouchEvent equivalent of keyboard action
        """
        # Arrow keys for page navigation
        if event.key == pygame.K_LEFT or event.key == pygame.K_PAGEUP:
            # Previous page
            return TouchEvent(GestureType.SWIPE_RIGHT, self.width // 2, self.height // 2)

        elif event.key == pygame.K_RIGHT or event.key == pygame.K_PAGEDOWN or event.key == pygame.K_SPACE:
            # Next page
            return TouchEvent(GestureType.SWIPE_LEFT, self.width // 2, self.height // 2)

        elif event.key == pygame.K_UP:
            # Scroll up (if applicable)
            return TouchEvent(GestureType.SWIPE_DOWN, self.width // 2, self.height // 2)

        elif event.key == pygame.K_DOWN:
            # Scroll down (if applicable)
            return TouchEvent(GestureType.SWIPE_UP, self.width // 2, self.height // 2)

        elif event.key == pygame.K_ESCAPE or event.key == pygame.K_q:
            # Quit
            logger.info("Quit via keyboard")
            self.running = False
            return None

        elif event.key == pygame.K_EQUALS or event.key == pygame.K_PLUS:
            # Zoom in (pinch out)
            return TouchEvent(GestureType.PINCH_OUT, self.width // 2, self.height // 2)

        elif event.key == pygame.K_MINUS:
            # Zoom out (pinch in)
            return TouchEvent(GestureType.PINCH_IN, self.width // 2, self.height // 2)

        return None

    async def set_brightness(self, level: int):
        """
        Set display brightness (not supported in Pygame).

        Args:
            level: Brightness level (0-10)

        Note: Brightness control is not available in Pygame.
        This is a no-op for desktop testing.
        """
        logger.debug(f"Brightness set to {level} (not supported in Pygame)")

    async def run_event_loop(self, app):
        """
        Run the Pygame event loop.

        Args:
            app: DReaderApplication instance

        This method:
        1. Initializes Pygame
        2. Starts the application
        3. Runs the event loop
        4. Handles events and updates display
        5. Shuts down gracefully
        """
        logger.info("Starting Pygame event loop")

        try:
            # Initialize
            await self.initialize()
            await app.start()

            self.running = True

            # Show instructions
            await self._show_instructions()
            await asyncio.sleep(2)

            # Main event loop
            clock = pygame.time.Clock()

            while self.running and app.is_running():
                # Process events
                touch_event = await self.get_touch_event()

                if touch_event:
                    # Handle touch event
                    await app.handle_touch(touch_event)

                # Cap frame rate
                clock.tick(60)  # 60 FPS max
                await asyncio.sleep(0.001)  # Yield to other async tasks

            logger.info("Event loop ended")

        except Exception as e:
            logger.error(f"Error in event loop: {e}", exc_info=True)
            raise

        finally:
            # Shutdown
            logger.info("Shutting down application")
            await app.shutdown()
            await self.cleanup()

    async def _show_instructions(self):
        """Show keyboard instructions overlay."""
        if not self.screen:
            return

        # Create instruction text
        font = pygame.font.Font(None, 24)
        instructions = [
            "DReader E-Book Reader",
            "",
            "Mouse Gestures:",
            "  Drag LEFT (horizontal) = Next Page",
            "  Drag RIGHT (horizontal) = Previous Page*",
            "  Drag UP (vertical) = Navigation/TOC Overlay",
            "  Drag DOWN (vertical) = Settings Overlay",
            "",
            "Keyboard Shortcuts:",
            "  Space / Right Arrow = Next Page",
            "  Left Arrow = Previous Page*",
            "  +/- = Font Size",
            "  Q/Escape = Quit",
            "",
            "*Previous page not working (pyWebLayout bug)",
            "",
            "Press any key to start..."
        ]

        # Create semi-transparent overlay
        overlay = pygame.Surface((self.width, self.height))
        overlay.fill((255, 255, 255))
        overlay.set_alpha(230)

        # Render text
        y = 100
        for line in instructions:
            if line:
                text = font.render(line, True, (0, 0, 0))
            else:
                text = pygame.Surface((1, 20))  # Empty line
            text_rect = text.get_rect(center=(self.width // 2, y))
            overlay.blit(text, text_rect)
            y += 30

        # Display
        self.screen.blit(overlay, (0, 0))
        pygame.display.flip()
