"""
Frame class for FMUS-VID.

This module provides the Frame class for handling individual video frames.
"""

from typing import Union, Tuple, Optional, Any
from pathlib import Path
import logging

logger = logging.getLogger(__name__)

class Frame:
    """
    Class for handling individual video frames.

    This class provides methods for manipulating and saving frames.
    """

    def __init__(self, frame_data: Any, backend: Any):
        """
        Initialize a Frame object.

        Args:
            frame_data: Backend-specific frame data
            backend: Backend instance
        """
        self._frame_data = frame_data
        self._backend = backend

    def save(self, path: Union[str, Path], **kwargs) -> None:
        """
        Save frame to file.

        Args:
            path: Output file path
            **kwargs: Save options

        Example:
            >>> frame.save("thumbnail.jpg", quality=95)
        """
        self._backend.save_frame(self._frame_data, path, **kwargs)

    def resize(self, width: Optional[int] = None, height: Optional[int] = None,
               keep_aspect: bool = True) -> 'Frame':
        """
        Resize frame to specified dimensions.

        Args:
            width: Target width (None to auto-calculate from height)
            height: Target height (None to auto-calculate from width)
            keep_aspect: Maintain aspect ratio if only one dimension is specified

        Returns:
            New Frame object

        Example:
            >>> thumbnail = frame.resize(width=320)
        """
        new_frame_data = self._backend.resize_frame(
            self._frame_data,
            width=width,
            height=height,
            keep_aspect=keep_aspect
        )
        return Frame(new_frame_data, self._backend)

    def crop(self, x: int, y: int, width: int, height: int) -> 'Frame':
        """
        Crop frame to specified region.

        Args:
            x: X coordinate of top-left corner
            y: Y coordinate of top-left corner
            width: Width of crop region
            height: Height of crop region

        Returns:
            New Frame object

        Example:
            >>> cropped = frame.crop(100, 100, 400, 300)
        """
        new_frame_data = self._backend.crop_frame(
            self._frame_data,
            x=x,
            y=y,
            width=width,
            height=height
        )
        return Frame(new_frame_data, self._backend)

    def grayscale(self) -> 'Frame':
        """
        Convert frame to grayscale.

        Returns:
            New Frame object

        Example:
            >>> bw = frame.grayscale()
        """
        new_frame_data = self._backend.grayscale_frame(self._frame_data)
        return Frame(new_frame_data, self._backend)

    def blur(self, radius: float) -> 'Frame':
        """
        Apply Gaussian blur with specified radius.

        Args:
            radius: Blur radius

        Returns:
            New Frame object

        Example:
            >>> blurred = frame.blur(5)
        """
        new_frame_data = self._backend.blur_frame(self._frame_data, radius=radius)
        return Frame(new_frame_data, self._backend)

    def brightness(self, factor: float) -> 'Frame':
        """
        Adjust frame brightness.

        Args:
            factor: Brightness factor (1.0 = original, 1.5 = +50%, 0.5 = -50%)

        Returns:
            New Frame object

        Example:
            >>> brighter = frame.brightness(1.2)
        """
        new_frame_data = self._backend.brightness_frame(self._frame_data, factor=factor)
        return Frame(new_frame_data, self._backend)

    def contrast(self, factor: float) -> 'Frame':
        """
        Adjust frame contrast.

        Args:
            factor: Contrast factor (1.0 = original, 1.5 = +50%, 0.5 = -50%)

        Returns:
            New Frame object

        Example:
            >>> higher_contrast = frame.contrast(1.2)
        """
        new_frame_data = self._backend.contrast_frame(self._frame_data, factor=factor)
        return Frame(new_frame_data, self._backend)

    def overlay(self, image, position: Tuple[int, int] = (0, 0),
                opacity: float = 1.0) -> 'Frame':
        """
        Overlay an image on the frame.

        Args:
            image: Image to overlay (Frame or PIL Image)
            position: (x, y) coordinates for placement
            opacity: Opacity of overlay (1.0 = fully opaque)

        Returns:
            New Frame object

        Example:
            >>> with_logo = frame.overlay(logo, position=(20, 20), opacity=0.8)
        """
        # If image is a Frame, extract its data
        if isinstance(image, Frame):
            image = image._frame_data

        new_frame_data = self._backend.overlay_frame(
            self._frame_data,
            image,
            position=position,
            opacity=opacity
        )
        return Frame(new_frame_data, self._backend)

    def add_text(self, text: str, position: Tuple[int, int],
                 font: str = "Arial", size: int = 24,
                 color: Union[str, Tuple[int, int, int]] = "white") -> 'Frame':
        """
        Add text to the frame.

        Args:
            text: Text to add
            position: (x, y) coordinates for placement
            font: Font name
            size: Font size
            color: Text color (name or RGB tuple)

        Returns:
            New Frame object

        Example:
            >>> with_caption = frame.add_text("Hello World", position=(50, 50), size=32)
        """
        new_frame_data = self._backend.add_text_to_frame(
            self._frame_data,
            text=text,
            position=position,
            font=font,
            size=size,
            color=color
        )
        return Frame(new_frame_data, self._backend)

    def to_pil(self):
        """
        Convert frame to PIL Image.

        Returns:
            PIL Image object

        Example:
            >>> pil_image = frame.to_pil()
            >>> pil_image.show()
        """
        return self._backend.frame_to_pil(self._frame_data)

    def to_numpy(self):
        """
        Convert frame to NumPy array.

        Returns:
            NumPy array (height, width, channels)

        Example:
            >>> import numpy as np
            >>> array = frame.to_numpy()
            >>> print(f"Shape: {array.shape}")
        """
        return self._backend.frame_to_numpy(self._frame_data)

    def __repr__(self) -> str:
        """String representation of the Frame object."""
        try:
            # Try to get frame dimensions
            width, height = self._backend.get_frame_dimensions(self._frame_data)
            return f"Frame({width}x{height})"
        except Exception:
            return "Frame(unknown dimensions)"
