from yta_editor_time.enum import SpecificationRequirement
from yta_editor_time.specifications.types import FrameIndex, FramesNumber
from yta_validation import PythonValidator
from dataclasses import dataclass
from typing import Union


@dataclass(frozen = True)
class FrameIndexSpecification:
    """
    The frames that must be affected according to
    this specification.

    Here you have some instructions about the
    different options you have:
    - Set `start_frame` and `end_frame` to apply
    the modification to the frames from the
    `start_frame` to the `end_frame` (including not
    this `end_frame`).
    - Set `start_frame` and `total_frames` to apply
    the modification from the `start_frame` and 
    lasting the `total_frames` provided.
    """

    start_frame: Union[FrameIndex, None]
    """
    The first frame that must be affected by this
    specification.
    """
    end_frame: Union[FrameIndex, None]
    """
    The last frame that must be affected by this
    specification.
    """
    total_frames: Union[FramesNumber, None]
    """
    The total amount of frames that this specification
    must last.
    """

    @property
    def requirements(
        self
    ) -> dict[SpecificationRequirement]:
        """
        The requirements of this specification that are
        needed to be able to resolve it.
        """
        return {
            SpecificationRequirement.TOTAL_FRAMES
        }

    def __post_init__(
        self
    ):
        if (
            not PythonValidator.is_instance_of(self.start_frame, FrameIndex) and
            self.start_frame is not None
        ):
            object.__setattr__(self, 'start_frame', FrameIndex(self.start_frame))
        if (
            not PythonValidator.is_instance_of(self.end_frame, FrameIndex) and
            self.end_frame is not None
        ):
            object.__setattr__(self, 'end_frame', FrameIndex(self.end_frame))
        if (
            not PythonValidator.is_instance_of(self.total_frames, FramesNumber) and
            self.total_frames is not None
        ):
            object.__setattr__(self, 'total_frames', FramesNumber(self.total_frames))

        # exactly two of three must be provided
        provided = sum(
            x is not None
            for x in (self.start_frame, self.end_frame, self.total_frames)
        )

        if provided != 2:
            raise ValueError(
                'Provide exactly two of: start_frame, end_frame, total_frames'
            )

        if (
            self.start_frame and
            self.end_frame
        ):
            if self.end_frame.value < self.start_frame.value:
                raise ValueError('end_frame must be >= start_frame')

        if (
            self.total_frames and
            self.total_frames.value == 0
        ):
            raise ValueError('total_frames must be > 0')