from yta_editor.transformations.effects.abstract import EffectParameters
from yta_editor.transformations.effects.video import VideoEffect
from yta_editor_nodes.processor.video.waving_frame import WavingFrameVideoNodeProcessor
from yta_editor_time.evaluation_context import EvaluationContext
from yta_editor_parameters.values.abstract import VideoEditorParameterValue
from yta_editor_parameters.values import ConstantVideoEditorParameterValue
from yta_validation.parameter import ParameterValidator
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 WavingFrameVideoEffectParameters(EffectParameters):
    """
    The parameters to use when applying this effect
    that turns the frames into waving frames for a
    specific `evaluation_context`.

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

    def __init__(
        self,
        amplitude: float,
        frequency: float,
        speed: float
    ):
        # TODO: Add docs
        self.amplitude: float = amplitude
        self.frequency: float = frequency
        self.speed: float = speed
        

class WavingFrameVideoEffect(VideoEffect):
    """
    The effect that will modify the frames and
    transform them into waving frames according
    to the provided conditions.
    """

    def __init__(
        self,
        do_use_gpu: bool = True,
        amplitude: VideoEditorParameterValue = ConstantVideoEditorParameterValue(0.05),
        frequency: VideoEditorParameterValue = ConstantVideoEditorParameterValue(10.0),
        speed: VideoEditorParameterValue = ConstantVideoEditorParameterValue(2.0)
    ):
        ParameterValidator.validate_mandatory_instance_of('amplitude', amplitude, VideoEditorParameterValue)
        ParameterValidator.validate_mandatory_instance_of('frequency', frequency, VideoEditorParameterValue)
        ParameterValidator.validate_mandatory_instance_of('speed', speed, VideoEditorParameterValue)

        self.do_use_gpu: bool = do_use_gpu
        """
        Flag to indicate if using GPU or not.
        """
        # TODO: Maybe 'parse_params' (?)
        self.amplitude: VideoEditorParameterValue = amplitude
        """
        The `VideoEditorParameterValue` that defines the value that
        should be applied for the `amplitude` and the specific
        `evaluation_context` provided.
        """
        self.frequency: VideoEditorParameterValue = frequency
        """
        The `VideoEditorParameterValue` that defines the value that
        should be applied for the `frequency` and the specific
        `evaluation_context` provided.
        """
        self.speed: VideoEditorParameterValue = speed
        """
        The `VideoEditorParameterValue` that defines the value that
        should be applied for the `speed` and the specific
        `evaluation_context` provided.
        """

    def _get_params_at(
        self,
        evaluation_context: EvaluationContext
    ) -> WavingFrameVideoEffectParameters:
        """
        *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)

        return WavingFrameVideoEffectParameters(
            amplitude = self.amplitude.evaluate(
                evaluation_context = evaluation_context
            ),
            frequency = self.frequency.evaluate(
                evaluation_context = evaluation_context
            ),
            speed = self.speed.evaluate(
                evaluation_context = evaluation_context
            )
        )
    
    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
        )

        # TODO: Is there any value that doesn't make any change (?)
        # if parameters.transparency == 1.0:
        #     return frame
        
        return WavingFrameVideoNodeProcessor(
            # TODO: What do I do with the 'opengl_context' (?)
            opengl_context = None
        ).process(
            inputs = {
                'base_input': frame
            },
            evaluation_context = evaluation_context,
            do_use_gpu = self.do_use_gpu,
            output_size = output_size,
            # parameters = parameters
            parameters = {
                'amplitude': parameters.amplitude,
                'frequency': parameters.frequency,
                'speed': parameters.speed
            }
        )