from yta_validation.parameter import ParameterValidator
from av.video.frame import VideoFrame
from av.audio.frame import AudioFrame
from typing import Union


IS_FROM_EMPTY_PART_METADATA = 'is_from_empty_part'
"""
Metadata key to indicate if the frame
has been generated by an empty part
and should be ignored when trying to
combine with others.
"""

class _FrameWrappedBase:
    """
    Class to wrap video and audio frames from
    the pyav library but to support a metadata
    field to inject some information we need
    when processing and combining them.
    """

    @property
    def is_from_empty_part(
        self
    ) -> bool:
        """
        Flag to indicate if the frame comes from
        an empty part or not, that will be done
        by checking the 'is_from_empty_part' 
        attribute in the metadata.
        """
        return IS_FROM_EMPTY_PART_METADATA in self.metadata

    def __init__(
        self,
        frame,
        metadata: dict = {}
    ):
        ParameterValidator.validate_mandatory_instance_of('frame', frame, [VideoFrame, AudioFrame])
        ParameterValidator.validate_mandatory_dict('metadata', metadata)

        self._frame: Union[VideoFrame, AudioFrame] = frame
        """
        The VideoFrame or AudioFrame pyav instance.
        """
        self.metadata: dict = metadata or {}
        """
        The metadata we want to include with the
        frame.
        """

    def __getattr__(
        self,
        name
    ):
        return getattr(self._frame, name)

    def __setattr__(
        self,
        name,
        value
    ):
        super().__setattr__(name, value)
        #setattr(self._frame, name, value)
        # if name in ('_frame', 'metadata'):
        #     super().__setattr__(name, value)
        # else:
        #     setattr(self._frame, name, value)

    def __repr__(
        self
    ):
        cname = self.__class__.__name__
        return f'<{cname} metadata={self.metadata} frame={self._frame!r}>'
    
    def set_as_from_empty_part(
        self
    ) -> None:
        """
        Add the metadata information to indicate
        that this is a frame that comes from an 
        empty part.
        """
        self.metadata[IS_FROM_EMPTY_PART_METADATA] = 'True'
    
    def unwrap(
        self
    ):
        """
        Get the original frame instance.
        """
        return self._frame

class VideoFrameWrapped(_FrameWrappedBase):
    """
    Class to wrap video frames from the pyav
    library but to support a metadata field
    to inject some information we need when
    processing and combining them.
    """

    def __init__(
        self,
        frame: VideoFrame,
        metadata: dict = {},
        is_from_empty_part: bool = False
    ):
        ParameterValidator.validate_mandatory_instance_of('frame', frame, VideoFrame)

        super().__init__(frame, metadata)

        if is_from_empty_part:
            self.set_as_from_empty_part()

class AudioFrameWrapped(_FrameWrappedBase):
    """
    Class to wrap audio frames from the pyav
    library but to support a metadata field
    to inject some information we need when
    processing and combining them.
    """

    def __init__(
        self,
        frame: AudioFrame,
        metadata: dict = {},
        is_from_empty_part: bool = False
    ):
        ParameterValidator.validate_mandatory_instance_of('frame', frame, AudioFrame)

        super().__init__(frame, metadata)

        if is_from_empty_part:
            self.set_as_from_empty_part()
