from fractions import Fraction
from ptulsconv.broadcast_timecode import TimecodeFormat
from typing import Tuple, List, Iterator


class SessionDescriptor:
    header: "HeaderDescriptor"
    files: List["FileDescriptor"]
    clips: List["ClipDescriptor"]
    plugins: List["PluginDescriptor"]
    tracks: List["TrackDescriptor"]
    markers: List["MarkerDescriptor"]

    def __init__(self, **kwargs):
        self.header = kwargs['header']
        self.files = kwargs['files']
        self.clips = kwargs['clips']
        self.plugins = kwargs['plugins']
        self.tracks = kwargs['tracks']
        self.markers = kwargs['markers']

    def markers_timed(self) -> Iterator[Tuple['MarkerDescriptor', Fraction]]:
        for marker in self.markers:
            marker_time = Fraction(marker.time_reference, int(self.header.sample_rate))
            #marker_time = self.header.convert_timecode(marker.location)
            yield marker, marker_time

    def tracks_clips(self) -> Iterator[Tuple['TrackDescriptor', 'TrackClipDescriptor']]:
        for track in self.tracks:
            for clip in track.clips:
                yield track, clip

    def track_clips_timed(self) -> Iterator[Tuple["TrackDescriptor", "TrackClipDescriptor",
                                                  Fraction, Fraction, Fraction]]:
        """
        :return: A Generator that yields track, clip, start time, finish time, and timestamp
        """
        for track, clip in self.tracks_clips():
            start_time = self.header.convert_timecode(clip.start_timecode)
            finish_time = self.header.convert_timecode(clip.finish_timecode)
            timestamp_time = self.header.convert_timecode(clip.timestamp) \
                if clip.timestamp is not None else None

            yield track, clip, start_time, finish_time, timestamp_time


class HeaderDescriptor:
    session_name: str
    sample_rate: float
    bit_depth: int
    start_timecode: str
    timecode_fps: str
    timecode_drop_frame: bool
    count_audio_tracks: int
    count_clips: int
    count_files: int

    def __init__(self, **kwargs):
        self.session_name = kwargs['session_name']
        self.sample_rate = kwargs['sample_rate']
        self.bit_depth = kwargs['bit_depth']
        self.start_timecode = kwargs['start_timecode']
        self.timecode_fps = kwargs['timecode_format']
        self.timecode_drop_frame = kwargs['timecode_drop_frame']
        self.count_audio_tracks = kwargs['count_audio_tracks']
        self.count_clips = kwargs['count_clips']
        self.count_files = kwargs['count_files']

    @property
    def timecode_format(self):
        return TimecodeFormat(frame_duration=self.frame_duration,
                              logical_fps=self.logical_fps,
                              drop_frame=self.timecode_drop_frame)

    def convert_timecode(self, tc_string: str) -> Fraction:
        return self.timecode_format.smpte_to_seconds(tc_string)

    @property
    def start_time(self) -> Fraction:
        """
        The start time of this session.
        :return: Start time in seconds
        """
        return self.convert_timecode(self.start_timecode)

    @property
    def logical_fps(self) -> int:
        return self._get_tc_format_params[0]

    @property
    def frame_duration(self) -> Fraction:
        return self._get_tc_format_params[1]

    @property
    def _get_tc_format_params(self) -> Tuple[int, Fraction]:
        frame_rates = {"23.976": (24, Fraction(1001, 24_000)),
                       "24": (24, Fraction(1, 24)),
                       "25": (25, Fraction(1, 25)),
                       "29.97": (30, Fraction(1001, 30_000)),
                       "30": (30, Fraction(1, 30)),
                       "59.94": (60, Fraction(1001, 60_000)),
                       "60": (60, Fraction(1, 60))
                       }

        if self.timecode_fps in frame_rates.keys():
            return frame_rates[self.timecode_fps]
        else:
            raise ValueError("Unrecognized TC rate (%s)" % self.timecode_format)


class TrackDescriptor:
    name: str
    comments: str
    user_delay_samples: int
    state: List[str]
    plugins: List[str]
    clips: List["TrackClipDescriptor"]

    def __init__(self, **kwargs):
        self.name = kwargs['name']
        self.comments = kwargs['comments']
        self.user_delay_samples = kwargs['user_delay_samples']
        self.state = kwargs['state']
        self.plugins = kwargs['plugins']
        self.clips = kwargs['clips']


class FileDescriptor(dict):
    pass


class TrackClipDescriptor:
    channel: int
    event: int
    clip_name: str
    start_timecode: str
    finish_timecode: str
    duration: str
    timestamp: str
    state: str

    def __init__(self, **kwargs):
        self.channel = kwargs['channel']
        self.event = kwargs['event']
        self.clip_name = kwargs['clip_name']
        self.start_timecode = kwargs['start_time']
        self.finish_timecode = kwargs['finish_time']
        self.duration = kwargs['duration']
        self.timestamp = kwargs['timestamp']
        self.state = kwargs['state']


class ClipDescriptor(dict):
    pass


class PluginDescriptor(dict):
    pass


class MarkerDescriptor:
    number: int
    location: str
    time_reference: int
    units: str
    name: str
    comments: str

    def __init__(self, **kwargs):
        self.number = kwargs['number']
        self.location = kwargs['location']
        self.time_reference = kwargs['time_reference']
        self.units = kwargs['units']
        self.name = kwargs['name']
        self.comments = kwargs['comments']
