"""
TODO: Maybe I have to move the content of this.
"""
# from yta_editor.tracks.items.abstract import TrackItemWithAudioMedia, _TrackItemWithVideo
from yta_editor.tracks.items.transition.mode import TransitionMode
from yta_editor.tracks.items.video import _VideoTrackItem
from yta_editor.tracks.items.abstract import _TrackItemWithVideo, _TrackItemWithAudio
from yta_editor_time.evaluation_context import EvaluationContext
from quicktions import Fraction
from typing import Union


class TransitionTrackItem(_TrackItemWithVideo, _TrackItemWithAudio):
    """
    A track item that is built by joining 2 different video
    items that are consecutive.
    """

    @property
    def copy(
        self
    ) -> '_TrackItem':
        """
        A copy of the current instance.
        """
        return TransitionTrackItem(
            track = self._track,
            t_start = self.t_start,
            duration = self.duration,
            transition_specification = self._transition_specification,
            mode = self.mode,
            item_in = self.item_in,
            item_out = self.item_out
        )
    
    @property
    def fps(
        self
    ) -> float:
        """
        The `fps` associated to the `track` this transition
        item belongs to.
        """
        return self._track.fps
    
    @property
    def output_size(
        self
    ) -> tuple[int, int]:
        """
        The `output_size` of the `track` that is associated to
        this transition item.
        """
        return self._track.output_size
    
    @property
    def is_unchanged(
        self
    ) -> bool:
        """
        *For internal use only*

        Internal boolean flag to indicate if the source has
        been modified (or not), useful when we want to 
        optimize the way we access to the frames to render.

        If the original source has not been modified, we
        don't need to read frame by frame and apply any
        change so we can do it faster.

        (!) This property is useless here as it is a
        transition.
        """
        return True

    def __init__(
        self,
        track: 'VideoTrack',
        t_start: Union[int, float, Fraction],
        # TODO: We need to be able to handle options
        # different than the 50% of each clip
        duration: Union[int, float, Fraction],
        transition_specification: Union['TransitionSpecification', None] = None,
        mode: TransitionMode = TransitionMode.TRIM,
        # TODO: This is a special one so this should be different
        item_in: Union['_AudioTrackItem', '_VideoTrackItem', 'TransitionTrackItem', 'GapTrackItem', None] = None,
        item_out: Union['_AudioTrackItem', '_VideoTrackItem', 'TransitionTrackItem', 'GapTrackItem', None] = None
    ):
        mode = TransitionMode.to_enum(mode)

        if transition_specification is None:
            # TODO: Move to top if possible
            from yta_editor.tracks.items.transition.transition_specification import TransitionSpecification

            transition_specification = TransitionSpecification.default()

        super().__init__(
            track = track,
            t_start = t_start,
            duration = duration,
            item_in = item_in,
            item_out = item_out,
            # TODO: I think I don't need these transition fields
            # in this kind of item
            transition_in = None,
            transition_out = None
        )

        # I keep this by now to be able to copy it
        self._transition_specification: TransitionSpecification = transition_specification
        """
        The specification of the node and the parameters we
        want to be applied as the transition.
        """
        self.mode: TransitionMode = mode
        """
        The mode in which the transition must be implemented.
        """

        # TODO: Due to cyclic import issue
        # TODO: Move to top if possible
        from yta_editor.utils.transition_calculator import _TransitionCalculator
        
        self._transition_calculator: _TransitionCalculator = _TransitionCalculator(
            transition_item = self,
            duration = self.duration,
            # TODO: What about this 'do_use_gpu' (?)
            do_use_gpu = True,
            transition_specification = transition_specification
        )
        """
        *For internal use only*

        A transition calculator to simplify the way we handle
        the calculations to obtain the frames we need.
        """

    def _get_audio_frames_at(
        self,
        t_track: Union[int, float, Fraction],
        # TODO: Ignore this, we have 'self.fps' here
        video_fps: Union[int, float, Fraction] = None,
        # TODO: Ignore this, we don't need it...
        do_use_source_limits: bool = True
    ) -> Union['AudioFrame', None]:
        """
        Get the audio frames of the provided `t_track`
        global time moment according to the internal
        configuration of the track item.

        TODO: This method must be overwritten by the
        specific class implementations.
        """
        # TODO: Is this the correct way? Is it generating
        # audio frames for a whole video frame (?)
        item_in_audio_frames = (
            list(self.item_in.get_audio_frames_at(
                t_track = t_track,
                # TODO: We should not receive the param
                do_use_source_limits = True
            ))
            if self.mode not in [TransitionMode.FREEZE_HEAD, TransitionMode.FREEZE_BOTH] else
            self._track.audio_silent
        )

        item_out_audio_frames = (
            list(self.item_out.get_audio_frames_at(
                # t_track = t_track + self.duration,
                t_track = t_track,
                # TODO: We should not receive the param
                do_use_source_limits = True
            ))
            if self.mode not in [TransitionMode.FREEZE_TAIL, TransitionMode.FREEZE_BOTH] else
            self._track.audio_silent
        )

        audio_frames = self._transition_calculator._get_process_audio_frames(
            audio_frames_a = item_in_audio_frames,
            audio_frames_b = item_out_audio_frames,
            evaluation_context = EvaluationContext.from_t(
                fps = self.fps,
                total_frames = int(self.duration * self.fps),
                t = self._t_track_to_t_track_item(t_track)
            )
        )

        # # TODO: Implement this as a combination of the audio
        # # coming from clips at the same time, but by now I'm
        # # just returning something
        # frames = [
        #     AudioFrameCombinator.sum_tracks_frames(
        #         tracks_frames = non_empty_collapsed_frames,
        #         sample_rate = self.audio_fps,
        #         # TODO: This was not being sent before
        #         layout = self.audio_layout,
        #         format = self.audio_format
        #     )
        # ]

        # for audio_frame in frames:
        #     yield audio_frame

        return audio_frames

        return item_in_audio_frames

    def _get_video_frame_at(
        self,
        t_track: Union[int, float, Fraction],
        do_use_source_limits: bool = False
    ) -> Union['VideoFrame', None]:
        """
        Get the frame of the transition for the global
        `t_track` time moment provided.
        """
        # TODO: The 't' must be the global value of the timeline
        # Obtain the frames from each video
        # TODO: I actually need to obtain a frame that will be
        # out of the current 'item_in' valid time interval, as
        # it has been trimmed because of this transition, and 
        # the part we are using is just after (for the
        # 'item_in', before for the 'item_out') the last part
        # we are showing in the video
        # TODO: Calculate the 't' based on the 'item_in' t_end
        # TODO: We need to read the 'item_in.t_end' + 'local_t'
        # for the transition

        """
        Imagine this context:
        - item_in: [0, 2)   Transition: [2, 3)   item_out: [3, 4)
        The global t=2.5 is actually the 0.5 inside the 
        transition, that should read the 'item_in.t_end+0.5' (out
        of the time interval that is being shown) of the item_in.

        Now imagine that the item_in is [0, 2) but the media 
        inside is actually [0, 8) and it is reading from [3, 5).
        That means that the transition for the global t=2.5, 
        is a local_t=0.5, should be reading the t=2.5 (out of
        time interval), that is actually the t_media=5.5.
        """

        """
        As the media has been enshortened due to the transition,
        we need to use `t_track` values that are out of the 
        current item media limits, thats why we force the values
        to be allowed with the `do_use_source_limits` parameter.

        But, as the transition could be based on frozen frames,
        we 
        """
        frame_item_in = (
            self.item_in.get_video_frame_at(
                t_track = t_track,
                do_use_source_limits = True
            )
            if self.mode not in [TransitionMode.FREEZE_HEAD, TransitionMode.FREEZE_BOTH] else
            # TODO: Return as the one on top is doing
            self.item_in._last_video_frame
        ).as_rgba_numpy

        frame_item_out = (
            self.item_out.get_video_frame_at(
                #t_track = t_track + self.duration,
                t_track = t_track,
                do_use_source_limits = True
            )
            if self.mode not in [TransitionMode.FREEZE_TAIL, TransitionMode.FREEZE_BOTH] else
            # TODO: Return as the one on top is doing
            self.item_out._first_video_frame
        ).as_rgba_numpy

        # TODO: I think I need to implement the mask here
        # by using the 'self.item_mask.get_video_frame_at'

        """
        *Representacion con t(global) de clips*
        Clip A: [0, 3)   Clip B: [3, 5)
        - Añadimos transition de 1s
        Clip A: [0, 2)   Transition: [2, 3)   Clip B: [3, 4)
        - La 'Transition' tiene [2, 3) de A y [0, 1) de B
        - El t(global)=2.5 ahora cae en la transición, que se
          convierte a t(local)=2.5-(3.0-1.0)=0.5, y dado que
          la duration es 1.0; 0.5 es el t_progress=0.5
        """
        frame = self._transition_calculator.process_frame(
            frame_a = frame_item_in,
            frame_b = frame_item_out,
            evaluation_context = EvaluationContext.from_t(
                fps = self.fps,
                total_frames = int(self.duration * self.fps),
                t = self._t_track_to_t_track_item(t_track)
            )
        )

        # TODO: Improve this
        from yta_editor.utils.frame_wrapper import VideoFrameWrapped

        return VideoFrameWrapped(frame)
    
    # TODO: What if I have another abstract class to inherit
    # from that is different having not the transitions 
    # instead of blocking these methods here? Maybe a 
    # TrackItem, TrackItemWithTransitions (?)
    #  TODO: Should I remove this (?)
    def set_transition_in(self, transition):
        raise Exception('This class can not have transitions')
    
    def set_transition_out(self, transition):
        raise Exception('This class can not have transitions')
    
    def unset_transition_in(self):
        raise Exception('This class can not have transitions')
    
    def unset_transition_out(self):
        raise Exception('This class can not have transitions')  
