from pathlib import Path
from typing import List

import yaml


def get_time(ms: int):
    s, ms = divmod(ms, 1000)
    m, s = divmod(s, 60)
    h, m = divmod(m, 60)
    return "%01d:%02d:%02d.%02d" % (h, m, s, ms // 10)


class Note:
    def __init__(
        self, position: int, duration: int, lyric: str, mspt: float = 1
    ) -> None:
        self.start: float = position * mspt
        self.dur: float = duration * mspt
        self.end: float = self.start + self.dur
        self.lyric: str = " " if lyric in {"AP", "SP"} else lyric

    def __add__(self, other):
        """append current note"""
        return Note(self.start, other.start + other.dur - self.start, self.lyric)


class UtA:
    def __init__(
        self, src_proj: str, src_text: str = "", perset: int = 0, offset: int = 0
    ):
        self.src = yaml.load(src_proj, Loader=yaml.FullLoader)
        self.src_text = src_text
        self.perset = perset
        self.offset = offset

        self.out = []
        self.index = -1
        self.mspt = 60000 / (self.src["bpm"] * self.src["resolution"])
        self.get_tracks()
        self.get_ass_line()

    @staticmethod
    def get_time(ms: int):
        s, ms = divmod(ms, 1000)
        m, s = divmod(s, 60)
        h, m = divmod(m, 60)
        return "%01d:%02d:%02d.%02d" % (h, m, s, ms // 10)

    def get_tracks(self):
        self.tracks: dict[str, List[Note]] = {}

        for track in self.src["voice_parts"]:
            notes: List[Note] = []
            for raw_note in track["notes"]:
                note = Note(
                    raw_note["position"],
                    raw_note["duration"],
                    raw_note["lyric"],
                    self.mspt,
                )
                if note.lyric in ["+"]:
                    note = notes.pop() + note
                notes.append(note)
            self.tracks[track["name"]] = notes

    def get_ass_line(self):
        perfix = "{\k" + str(int(self.perset // 10)) + "} " if self.perset else ""
        suffix = "{\k" + str(int(self.offset // 10)) + "} " if self.offset else ""
        for track_name in self.tracks.keys():
            notes = self.tracks[track_name]
            text = perfix
            start = 0
            for index, note in enumerate(notes):
                # check is empty
                if note.lyric == " " and text == perfix:
                    continue
                # get start time
                if not start:
                    start = note.start - self.perset
                # check is new line
                if (
                    index > 0
                    and note.start > notes[index - 1].end + 1  # prevent float error
                ):
                    self.out.append(
                        f"Dialogue: 0,{self.get_time(start)},{self.get_time(notes[index-1].end+self.offset)},Default,{track_name},0,0,0,,{text+suffix}"
                    )
                    start = note.start - self.perset
                    text = perfix
                    if note.lyric == " ":
                        start = 0
                        continue
                text += "{\k" + str(int(note.dur // 10)) + "}" + note.lyric
            self.out.append(
                f"Dialogue: 0,{self.get_time(start)},{self.get_time(note.end+self.offset)},Default,{track_name},0,0,0,,{text+suffix}"
            )

    def write(self, des: Path):
        des.parent.mkdir(0o755, parents=True, exist_ok=True)
        des.write_text(
            "\n".join(
                [
                    "[Script Info]",
                    "; Script generated by Aegisub 3.2.2",
                    "; http://www.aegisub.org/",
                    "Title: Default Aegisub file",
                    "ScriptType: v4.00+",
                    "WrapStyle: 0",
                    "ScaledBorderAndShadow: yes",
                    "YCbCr Matrix: None\n",
                    "[Events]",
                    "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text",
                ]
                + self.out
            ),
            "utf-8",
        )


# just for test
# UtA(
#    Path("能解答一切的答案.ustx").read_text("utf-8"),
#    perset=150
# ).write(Path("能解答一切的答案.ass"))
