"""
Hardware Abstraction Layer (HAL) interface for DReader.

This module defines the abstract interface that platform-specific
display/input implementations must provide.

The HAL separates the core e-reader logic from platform-specific
hardware details (display, touch input, buttons, etc.).
"""

from abc import ABC, abstractmethod
from typing import AsyncIterator, Optional
from PIL import Image

from .gesture import TouchEvent


class DisplayHAL(ABC):
    """
    Abstract interface for display and input hardware.

    Platform-specific implementations should subclass this and provide
    concrete implementations for all abstract methods.

    The HAL is responsible for:
    - Displaying images on the screen
    - Capturing touch/click input and converting to TouchEvent
    - Hardware-specific features (brightness, sleep, etc.)

    All methods are async to support non-blocking I/O.
    """

    @abstractmethod
    async def show_image(self, image: Image.Image):
        """
        Display a PIL Image on the screen.

        Args:
            image: PIL Image to display

        This method should handle:
        - Converting image format if needed for the display
        - Scaling/cropping if image size doesn't match display
        - Updating the physical display hardware
        """
        pass

    @abstractmethod
    async def get_touch_event(self) -> Optional[TouchEvent]:
        """
        Wait for and return the next touch event.

        Returns:
            TouchEvent if available, None if no event (non-blocking mode)

        This method should:
        - Read from touch hardware
        - Convert raw coordinates to TouchEvent
        - Detect gesture type (tap, swipe, etc.)
        - Return None immediately if no event available

        Note: For blocking behavior, implement a loop that awaits this
        method in the main event loop.
        """
        pass

    @abstractmethod
    async def set_brightness(self, level: int):
        """
        Set display brightness.

        Args:
            level: Brightness level (0-10, where 0=dimmest, 10=brightest)

        Platform implementations should map this to their hardware's
        actual brightness range.
        """
        pass

    async def initialize(self):
        """
        Initialize the display hardware.

        This optional method is called once before the application starts.
        Override to perform platform-specific initialization.
        """
        pass

    async def cleanup(self):
        """
        Clean up display hardware resources.

        This optional method is called during application shutdown.
        Override to perform platform-specific cleanup.
        """
        pass

    async def show_message(self, message: str, duration: float = 2.0):
        """
        Display a text message (for loading screens, errors, etc.).

        Args:
            message: Text message to display
            duration: How long to show message (seconds)

        Default implementation creates a simple text image.
        Override for platform-specific message display.
        """
        from PIL import ImageDraw, ImageFont

        # Create simple text image
        img = Image.new('RGB', (800, 1200), color=(255, 255, 255))
        draw = ImageDraw.Draw(img)

        # Try to use a decent font, fall back to default
        try:
            font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 32)
        except:
            font = ImageFont.load_default()

        # Draw centered text
        bbox = draw.textbbox((0, 0), message, font=font)
        text_width = bbox[2] - bbox[0]
        text_height = bbox[3] - bbox[1]
        x = (800 - text_width) // 2
        y = (1200 - text_height) // 2

        draw.text((x, y), message, fill=(0, 0, 0), font=font)

        await self.show_image(img)

        # Wait for duration
        if duration > 0:
            import asyncio
            await asyncio.sleep(duration)


class KeyboardInputHAL(ABC):
    """
    Optional abstract interface for keyboard input.

    This is separate from DisplayHAL to support platforms that have
    both touch and keyboard input (e.g., desktop testing).
    """

    @abstractmethod
    async def get_key_event(self) -> Optional[str]:
        """
        Get the next keyboard event.

        Returns:
            Key name as string (e.g., "up", "down", "enter", "q")
            None if no key event available
        """
        pass


class EventLoopHAL(DisplayHAL):
    """
    Extended HAL interface that provides its own event loop.

    Some platforms (e.g., Pygame, Qt) have their own event loop that
    must be used. This interface allows the HAL to run the main loop
    and call back to the application.

    Usage:
        hal = MyEventLoopHAL()
        app = DReaderApplication(AppConfig(display_hal=hal, ...))

        await hal.run_event_loop(app)
    """

    @abstractmethod
    async def run_event_loop(self, app):
        """
        Run the platform's event loop.

        Args:
            app: DReaderApplication instance to send events to

        This method should:
        1. Initialize the display
        2. Call app.start()
        3. Enter event loop
        4. Call app.handle_touch(event) for each event
        5. Handle quit events and call app.shutdown()
        """
        pass
