from yta_editor.media.video import VideoFileMedia
from yta_editor.transformations.effects.abstract import EffectParameters
from yta_editor.transformations.effects.video import VideoEffect
from yta_editor_nodes.processor.video.alphapedia_mask_reveal import AlphaPediaMaskRevealVideoNodeProcessor
from yta_editor_time.evaluation_context import EvaluationContext
from yta_validation.parameter import ParameterValidator
from yta_validation import PythonValidator
from typing import Union
from dataclasses import dataclass


# TODO: We need to define all the specific effect
# params to be able to build them properly missing
# no fields
@dataclass
class AlphaPediaMaskRevealVideoEffectParameters(EffectParameters):
    """
    The parameters to use when applying this effect
    that reveals a video with an alpha pedia mask for
    a specific `evaluation_context`.

    This will be returned by the effect when
    calculated, for a specific `evaluation_context`.
    """

    def __init__(
        self,
        mask_frame: Union['np.ndarray', 'moderngl.Texture']
    ):
        # TODO: Add doc
        self.mask_frame: Union['np.ndarray', 'moderngl.Texture'] = mask_frame
        """
        The frame that must be used to be applied as
        the mask frame.
        """
        

class AlphaPediaMaskRevealVideoEffect(VideoEffect):
    """
    The effect that will modify the video by
    revealing it with an alpha video mask according
    to the provided conditions.
    """

    def __init__(
        self,
        mask_input_filename: str,
        do_use_gpu: bool = True
    ):
        self.do_use_gpu: bool = do_use_gpu
        """
        Flag to indicate if using GPU or not.
        """
        self._mask_video: VideoFileMedia = VideoFileMedia(
            filename = mask_input_filename
        )
        """
        *For internal use only*

        The alpha video that we will use to read the
        frames and apply the mask.
        """
        self._node_processor = AlphaPediaMaskRevealVideoNodeProcessor(
            # TODO: What do I do with the 'opengl_context' (?)
            opengl_context = None
        )
        """
        *For internal use only*

        The node processor that we will use to process
        the input frame with the corresponding mask
        frames.
        """

    def _get_params_at(
        self,
        evaluation_context: EvaluationContext
    ) -> AlphaPediaMaskRevealVideoEffectParameters:
        """
        *For internal use only*

        Get the parameters that must be applied at the given
        `evaluation_context`.
        """
        ParameterValidator.validate_mandatory_instance_of('evaluation_context', evaluation_context, EvaluationContext)

        # TODO: This is repeated in the 'transition_calculator.py'
        # TODO: What about an easing function (?)
        t_transition_media = evaluation_context.progress.value * (self._mask_video.number_of_frames - 1) / self._mask_video.fps

        video_frame = self._mask_video.get_video_frame_at(
            t_media = t_transition_media
        ).to_ndarray(format = 'rgba')
        
        video_frame = (
            video_frame.frame
            if PythonValidator.is_instance_of(video_frame, 'VideoFrame') else
            video_frame
        )

        return AlphaPediaMaskRevealVideoEffectParameters(
            mask_frame = video_frame
        )
    
    def _apply(
        self,
        # TODO: Set the type
        frame: any,
        output_size: Union[tuple[int, int], None],
        evaluation_context: EvaluationContext
    # TODO: Set the type
    ) -> any:
        parameters = self._get_params_at(
            evaluation_context = evaluation_context
        )

        return self._node_processor.process(
            inputs = {
                'base_input': frame,
                'mask_input': parameters.mask_frame
            },
            evaluation_context = evaluation_context,
            do_use_gpu = self.do_use_gpu,
            output_size = output_size
        )