"""Conversion time module"""

from fractions import Fraction


class Convert:
    """Collection of methods to perform time conversion"""

    @classmethod
    def ts2f(cls, ts: str, fps: Fraction, /) -> int:
        """
        Convert a timestamp hh:mm:ss.xxxx in number of frames

        :param ts:          Timestamp
        :param fps:         Framerate Per Second
        :return:            Frames
        """
        s = cls.ts2seconds(ts)
        f = cls.seconds2f(s, fps)
        return f

    @classmethod
    def f2ts(cls, f: int, fps: Fraction, /, *, precision: int = 3) -> str:
        """
        Convert frames in timestamp hh:mm:ss.xxxx

        :param f:           Frames
        :param fps:         Framerate Per Second
        :param precision:   Precision number, defaults to 3
        :return:            Timestamp
        """
        s = cls.f2seconds(f, fps)
        ts = cls.seconds2ts(s, precision=precision)
        return ts

    @classmethod
    def seconds2ts(cls, s: float, /, *, precision: int = 3) -> str:
        """
        Convert seconds in timestamp hh:mm:ss.xxx

        :param s:           Seconds
        :param precision:   Precision number, defaults to 3
        :return:            Timestamp
        """
        m = s // 60
        s %= 60
        h = m // 60
        m %= 60
        return cls.composets(h, m, s, precision=precision)

    @classmethod
    def f2assts(cls, f: int, fps: Fraction, /) -> str:
        """
        Convert frames to .ass timestamp hh:mm:ss.xx properly
        by removing half of one frame per second of the specified framerate

        :param f:           Frames
        :param fps:         Framerate Per Second
        :return:            ASS timestamp
        """
        s = cls.f2seconds(f, fps)
        s -= fps ** -1 * 0.5
        ts = cls.seconds2ts(max(0, s), precision=3)
        return ts[:-1]

    @classmethod
    def assts2f(cls, assts: str, fps: Fraction, /) -> int:
        """
        Convert .ass timestamp hh:mm:ss.xx to frames properly
        by adding half of one frame per second of the specified framerate

        :param assts:       ASS timestamp
        :param fps:         Framerate Per Second
        :return:            Frames
        """
        s = cls.ts2seconds(assts)
        if s > 0:
            s += fps ** -1 * 0.5
        return cls.seconds2f(s, fps)

    @staticmethod
    def f2seconds(f: int, fps: Fraction, /) -> float:
        """
        Convert frames to seconds

        :param f:           Frames
        :param fps:         Framerate Per Second
        :return:            Seconds
        """
        if f == 0:
            return 0.0

        t = round(float(10 ** 9 * f * fps ** -1))
        s = t / 10 ** 9
        return s

    @staticmethod
    def ts2seconds(ts: str, /) -> float:
        """
        Convert timestamp hh:mm:ss.xxxx to seconds

        :param ts:          Timestamp
        :return:            Seconds
        """
        h, m, s = map(float, ts.split(':'))
        return h * 3600 + m * 60 + s

    @staticmethod
    def seconds2f(s: float, fps: Fraction, /) -> int:
        """
        Convert seconds to frames

        :param s:           Seconds
        :param fps:         Framerate Per Second
        :return:            Frames
        """
        return round(s * fps)

    @staticmethod
    def samples2seconds(num_samples: int, sample_rate: int, /) -> float:
        """
        Convert samples to seconds

        :param num_samples: Samples
        :param sample_rate: Playback sample rate
        :return:            Seconds
        """
        return num_samples / sample_rate

    @staticmethod
    def seconds2samples(s: float, sample_rate: int, /) -> int:
        """
        Convert seconds to samples

        :param s:           Seconds
        :param sample_rate: Playback sample rate
        :return:            Samples
        """
        return round(s * sample_rate)

    @classmethod
    def f2samples(cls, f: int, fps: Fraction, sample_rate: int) -> int:
        """
        Convert frames to samples

        :param f:           Frames
        :param fps:         Framerate Per Second
        :param sample_rate: Playback sample rate
        :return:            Samples
        """
        s = cls.f2seconds(f, fps)
        return cls.seconds2samples(s, sample_rate)

    @classmethod
    def samples2f(cls, num_samples: int, sample_rate: int, fps: Fraction) -> int:
        """
        Convert sample to frames

        :param num_samples: Samples
        :param sample_rate: Playback sample rate
        :param fps:         Framerate Per Second
        :return:            Frame
        """
        s = cls.samples2seconds(num_samples, sample_rate)
        return cls.seconds2f(s, fps)

    @staticmethod
    def composets(h: float, m: float, s: float, /, *, precision: int = 3) -> str:
        """
        Make a timestamp based on given hours, minutes and seconds

        :param h:           Hours
        :param m:           Minutes
        :param s:           Seconds
        :param precision:   Precision number, defaults to 3
        :return:            Timestamp
        """
        if precision == 0:
            out = f"{h:02.0f}:{m:02.0f}:{round(s):02}"
        elif precision == 3:
            out = f"{h:02.0f}:{m:02.0f}:{s:06.3f}"
        elif precision == 6:
            out = f"{h:02.0f}:{m:02.0f}:{s:09.6f}"
        elif precision == 9:
            out = f"{h:02.0f}:{m:02.0f}:{s:012.9f}"
        else:
            raise ValueError(f'composets: the precision {precision} must be a multiple of 3 (including 0)')
        return out
