from yta_editor.utils import VideoUtils
from yta_editor_frame.frame import Frame
from yta_validation.parameter import ParameterValidator
from av.video.frame import VideoFrame
from av.audio.frame import AudioFrame
from typing import Union

import numpy as np


IS_FROM_GAP_METADATA = 'is_from_gap'
"""
Metadata key to indicate if the frame
has been generated by a gap and should
be ignored when trying to combine with
others.
"""
IS_SILENT_METADATA = 'is_silent'
"""
Metadata key to indicate if the frame is a
silent frame or not, being able to handle it
properly when we need to combine it.
"""

# TODO: These classes will disappear because the
# new 'Frame' and 'AudioFrame' have metadata
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 frame(
        self
    ) -> Frame:
        """
        Get the original pyav VideoFrame wrapped in
        a `Frame` instance.
        """
        return self._frame

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

        This will return True only if the value
        is set and it is set as 'True'.
        """
        return self._is_metadata_boolean_set(IS_FROM_GAP_METADATA)
    
    @property
    def is_silent(
        self
    ) -> bool:
        """
        Flag to indicate if the frame that comes is
        actually an empty frame or not, which could
        be useful when we need to combine it with 
        others, and let us avoid reading the content
        to detect if it is silent or not.

        This metadata is valid for audio frames.

        This will return `True` only if the value is
        set and its value is `'True'`.
        """
        return self._is_metadata_boolean_set(IS_SILENT_METADATA)

    def __init__(
        self,
        # TODO: Maybe rename 'Frame' to 'VideoFrameBuffer'
        frame: Union[Frame, AudioFrame],
        metadata: dict = {}
    ):
        ParameterValidator.validate_mandatory_instance_of('frame', frame, [Frame, AudioFrame])
        ParameterValidator.validate_mandatory_dict('metadata', metadata)

        self._frame: Union[Frame, AudioFrame] = frame
        """
        The `Frame` (wrapping a pyav video frame), or
        an `AudioFrame` pyav instance.
        """
        # self.metadata: dict = metadata or {}
        # """
        # The metadata we want to include with the
        # frame.
        # """

    def __repr__(
        self
    ):
        cname = self.__class__.__name__

        return f'<{cname} metadata={self._frame.metadata} frame={self._frame!r}>'
    
    def set_is_from_gap(
        self,
        value: bool = True
    ) -> None:
        """
        Add the metadata information to indicate
        that this is a frame that comes from an 
        empty part.
        """
        return self._set_metadata_boolean(
            attribute_name = IS_FROM_GAP_METADATA,
            value = value
        )

    def set_is_silent(
        self,
        value: bool = True
    ) -> None:
        """
        Add the metadata information to indicate that this
        is a silent frame, including the `is_silent` 
        metadata.
        """
        return self._set_metadata_boolean(
            attribute_name = IS_SILENT_METADATA,
            value = value
        )

    def _is_metadata_boolean_set(
        self,
        attribute_name: str
    ) -> bool:
        """
        *For internal use only*

        Method to simplify the way we check if the boolean
        metadata `attribute_name` is set or not. A metadata
        boolean parameter must be stored as string `'True`'
        to be considered valid.
        """
        # is_parameter = self._frame.metadata.get(attribute_name, None)
        is_parameter = self.metadata.get(attribute_name, None)

        return (
            is_parameter == 'True'
            if is_parameter is not None else
            is_parameter
        )
    
    def _set_metadata_boolean(
        self,
        attribute_name: str,
        value: bool = True
    ) -> None:
        """
        Set the `attribute_name` metadata boolean parameter
        `value` provided (that will be stored as a str).
        """
        value = (
            'True'
            if value else
            'False'
        )

        self._frame.metadata.__setattr__(attribute_name, value)
    
# TODO: Maybe move? But careful with
# cyclic import issues
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.
    """

    @property
    def copy(
        self
    ) -> 'AudioFrameWrapped':
        """
        A copy of this same instance.
        """
        return AudioFrameWrapped(
            # TODO: Do I need to hard-copy this '_frame' (?)
            frame = self._frame,
            # metadata = self._frame.metadata
            metadata = self.metadata
        )

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

        super().__init__(
            frame = frame,
            metadata = metadata
        )

        # TODO: Remoe this when refactored
        self.metadata = {}

        self.set_is_from_gap(is_from_gap)
        self.set_is_silent(is_silent)

    # TODO: This need to be refactored, by now is
    # just to be able to make it work
    def set_is_from_gap(
        self,
        value: bool
    ) -> None:
        self.metadata['is_from_gap'] = (
            'True'
            if value else
            'False'
        )

    def set_is_silent(
        self,
        value: bool
    ) -> None:
        self.metadata['is_silent'] = (
            'True'
            if value else
            'False'
        )