from yta_video_frame_time.t_media import TMedia
from yta_editor_time.specifications.types import FrameIndex, TimeMoment, ProgressValue, FramesNumber, Fps, TimeDuration
from yta_validation import PythonValidator
from quicktions import Fraction
from dataclasses import dataclass, field
from typing import Union


# TODO: Maybe this should be moved to another project (?)
@dataclass(frozen = True)
class EvaluationContext:
    """
    A context to be evaluated in order to make
    calculations to apply effects, transitions, etc.

    Each instance is related to an specific scope
    and don't need information about the rest.
    """

    fps: Fps = Fps(60.0)
    """
    The frames per second.
    """
    total_frames: FramesNumber = FramesNumber(60)
    """
    The total amount of frames, which is also the
    duration but in number of frames.
    """
    frame_index: FrameIndex = FrameIndex(0)
    """
    The frame index, which is the most exact value.

    This value is the only source of truth for the
    whole evaluation context.
    """
    t: TimeMoment = field(init = False)
    """
    The time moment, truncated and multiple of `1/fps`
    that corresponds to the `frame_index`.

    This value is autocalculated in the `__post_init__`
    method.

    The formula:
    - `frame_index * (1 / fps)`
    """
    progress: ProgressValue = field(init = False)
    """
    The progress normalized that corresponds to the
    given `frame_index` according to the `total_frames`
    given.

    This value is autocalculated in the `__post_init__`
    method.

    The formula:
    - `frame_index / (duration_in_frames - 1)`
    """
    duration: TimeDuration = field(init = False)
    """
    The duration as a `Fraction` value.

    This value is autocalculated in the `__post_init__`
    method.

    The formula:
    - `total_frames * (1 / fps)`
    """

    def __post_init__(
        self
    ):
        if not PythonValidator.is_instance_of(self.fps, Fps):
            object.__setattr__(self, 'fps', Fps(self.fps))
        if not PythonValidator.is_instance_of(self.total_frames, FramesNumber):
            object.__setattr__(self, 'total_frames', FramesNumber(self.total_frames))
        if not PythonValidator.is_instance_of(self.frame_index, FrameIndex):
            object.__setattr__(self, 'frame_index', FrameIndex(self.frame_index))
        
        t_media = TMedia.from_frame_index(
            frame_index = int(self.frame_index),
            fps = float(self.fps)
        ).t
        progress_normalized = TMedia.to_progress_normalized(
            t = t_media,
            # TODO: Maybe in frames (?)
            duration = int(self.total_frames) / float(self.fps),
            fps = float(self.fps)
        )
        duration = TMedia.from_frame_index(
            frame_index = int(self.total_frames),
            fps = float(self.fps)
        ).t

        object.__setattr__(self, 't', TimeMoment(t_media))
        object.__setattr__(self, 'progress', ProgressValue(progress_normalized))
        object.__setattr__(self, 'duration', TimeDuration(duration))

    @classmethod
    def from_t(
        cls,
        fps: float,
        total_frames: int,
        t: Union[float, Fraction]
    ) -> 'EvaluationContext':
        """
        Get a new `EvaluationContext` instance by using
        the `t` time moment provided with the `fps` and
        `total_frames` also included.
        """
        return cls(
            fps = fps,
            total_frames = total_frames,
            frame_index = TMedia(
                t = t,
                fps = fps
            ).frame_index
        )