import os
import sys
import copy
import enum
import json
import time
import random
import shutil
import zipfile
from random import random as rand
from typing import List, Tuple, Union

from PyQt6 import QtGui, QtCore, QtWidgets, QtMultimedia, QtMultimediaWidgets


class InteractiveVariable:
    """
    互动节点的变量
    """

    def __init__(
        self,
        name: str,
        var_id: str,
        var_value: int,
        show: bool = False,
        random: bool = False,
    ):
        """
        Args:
            name      (str)  : 变量名
            var_id    (str)  : 变量 id
            var_value (int)  : 变量的值
            show      (bool) : 是否显示
            random    (bool) : 是否为随机值(1-100)
        """
        self.__var_id = var_id
        self.__var_value = var_value
        self.__name = name
        self.__is_show = show
        self.__random = random

    def get_id(self) -> str:
        return self.__var_id

    def refresh_value(self) -> None:
        """
        刷新变量数值
        """
        if self.is_random():
            self.__var_value = int(rand(0, 100))

    def get_value(self) -> int:
        return self.__var_value

    def is_show(self) -> bool:
        return self.__is_show

    def is_random(self) -> bool:
        return self.__random

    def get_name(self) -> str:
        return self.__name

    def __str__(self):
        return f"{self.__name} {self.__var_value}"


class InteractiveButtonAlign(enum.Enum):
    """
    按钮的文字在按钮中的位置


    ``` text
    -----
    |xxx|----o (TEXT_LEFT)
    -----

         -----
    o----|xxx| (TEXT_RIGHT)
         -----

    ----------
    |XXXXXXXX| (DEFAULT)
    ----------
    ```

    - DEFAULT
    - TEXT_UP
    - TEXT_RIGHT
    - TEXT_DOWN
    - TEXT_LEFT
    """

    DEFAULT = 0
    TEXT_UP = 1
    TEXT_RIGHT = 2
    TEXT_DOWN = 3
    TEXT_LEFT = 4


class InteractiveButton:
    """
    互动视频节点按钮类
    """

    def __init__(
        self,
        text: str,
        x: int,
        y: int,
        align: Union[InteractiveButtonAlign, int] = InteractiveButtonAlign.DEFAULT,
    ):
        """
        Args:
            text  (str)                         : 文字
            x     (int)                         : x 轴
            y     (int)                         : y 轴
            align (InteractiveButtonAlign | int): 按钮的文字在按钮中的位置
        """
        self.__text = text
        self.__pos = (x, y)
        if isinstance(align, InteractiveButtonAlign):
            align = align.value
        self.__align = align

    def get_text(self) -> str:
        return self.__text

    def get_align(self) -> int:
        return self.__align  # type: ignore

    def get_pos(self) -> Tuple[int, int]:
        return self.__pos

    def __str__(self):
        return f"{self.__text} {self.__pos}"


class InteractiveJumpingCondition:
    """
    节点跳转的公式,只有公式成立才会跳转
    """

    def __init__(self, var: List[InteractiveVariable] = [], condition: str = "True"):
        """
        Args:
            var       (List[InteractiveVariable]): 所有变量
            condition (str)                      : 公式
        """
        self.__vars = var
        self.__command = condition

    def get_result(self) -> bool:
        """
        计算公式获得结果

        Returns:
            bool: 是否成立
        """
        if self.__command == "":
            return True
        command = copy.copy(self.__command)
        for var in self.__vars:
            var_name = var.get_id()
            var_value = var.get_value()
            command = command.replace(var_name, str(var_value))
        command = command.replace("&&", " and ")
        command = command.replace("||", " or ")
        command = command.replace("!", " not ")
        command = command.replace("===", "==")
        command = command.replace("!==", "!=")
        command = command.replace("true", "True")
        command = command.replace("false", "False")
        return eval(command)

    def __str__(self):
        return f"{self.__command}"


class InteractiveJumpingCommand:
    """
    节点跳转对变量的操作
    """

    def __init__(self, var: List[InteractiveVariable] = [], command: str = ""):
        """
        Args:
            var       (List[InteractiveVariable]): 所有变量
            condition (str)                      : 公式
        """
        self.__vars = var
        self.__command = command

    def run_command(self) -> List["InteractiveVariable"]:
        """
        执行操作

        Returns:
            List[InteractiveVariable]
        """
        if self.__command == "":
            return self.__vars
        for code in self.__command.split(";"):
            var_name_ = code.split("=")[0]
            var_new_value = code.split("=")[1]
            for var in self.__vars:
                var_name = var.get_id()
                var_value = var.get_value()
                var_new_value = var_new_value.replace(var_name, str(var_value))
            var_new_value_calc = eval(var_new_value)
            for var in self.__vars:
                if var.get_id() == var_name_:
                    var._InteractiveVariable__var_value = var_new_value_calc  # type: ignore
        return self.__vars


class InteractiveNodeJumpingType(enum.Enum):
    """
    对下一节点的跳转的方式

    - ASK    : 选择
    - DEFAULT: 跳转到默认节点
    - READY  : 选择(只有一个选择)
    """

    READY = 1
    DEFAULT = 0
    ASK = 2


class Button:
    def __init__(self, id_, pos, text, condition, command):
        # A class that provides the button model.
        self.node_id = id_
        self.pos = pos
        self.text = text
        self.condition = condition
        self.command = command
        # 什么?别问我为什么不用 TypedDict / dataclass

    def __str__(self) -> str:
        return f"{self.pos} {self.text} {self.condition} {self.command}"


class ButtonLabel(QtWidgets.QLabel):
    def __init__(self, parent: QtWidgets.QWidget = None):
        super().__init__(parent=parent)
        self.setObjectName(str(time.time()))

    def prep_text(self, text: str, x: int, y: int):
        self.setText(text)
        self.setWordWrap(True)
        self.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        font = QtGui.QFont()
        font.setPointSize(12)
        font.setBold(True)
        self.setFont(font)
        rect = QtCore.QRect(x, y, 200, 50)
        self.setGeometry(rect)
        self.setStyleSheet(
            "border-width: 5px;\
             border-style: solid;\
             border-color: rgb(100, 100, 100);\
             background-color: rgb(50, 50, 50);\
             color: rgb(255, 255, 255);"
        )
        self.raise_()
        return self


class MPlayer(object):
    def setup(self, Form):
        # UI
        Form.setObjectName("Form")
        Form.resize(800, 600)
        sizePolicy = QtWidgets.QSizePolicy(
            QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed
        )
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth())
        Form.setSizePolicy(sizePolicy)
        Form.setMinimumSize(QtCore.QSize(800, 650))
        Form.setMaximumSize(QtCore.QSize(800, 650))
        Form.setBaseSize(QtCore.QSize(800, 650))
        Form.setWindowTitle("MPlayer")
        self.win: QtWidgets.QWidget = Form
        self.player = QtMultimediaWidgets.QVideoWidget(Form)
        self.player.setGeometry(QtCore.QRect(0, 0, 800, 450))
        self.player.setObjectName("player")
        self.mediaplayer = QtMultimedia.QMediaPlayer()
        self.mediaplayer.setVideoOutput(self.player)
        self.audio_output = QtMultimedia.QAudioOutput()
        self.audio_output.setVolume(0.0)
        self.mediaplayer.setAudioOutput(self.audio_output)
        self.slider = QtWidgets.QSlider(Form)
        self.slider.setGeometry(QtCore.QRect(120, 455, 571, 22))
        self.slider.setOrientation(QtCore.Qt.Orientation.Horizontal)
        self.slider.setObjectName("slider")
        self.slider.setValue(100)
        self.pp = QtWidgets.QPushButton(Form)
        self.pp.setGeometry(QtCore.QRect(0, 450, 113, 32))
        self.pp.setObjectName("pp")
        self.pushButton = QtWidgets.QPushButton(Form)
        self.pushButton.setGeometry(QtCore.QRect(0, 525, 113, 32))
        self.pushButton.setObjectName("pushButton")
        self.node = QtWidgets.QLabel(Form)
        self.node.setGeometry(QtCore.QRect(120, 530, 191, 16))
        self.node.setObjectName("node")
        self.info = QtWidgets.QLabel(Form)
        self.info.setGeometry(QtCore.QRect(320, 520, 471, 36))
        self.info.setWordWrap(True)
        font = QtGui.QFont()
        font.setBold(True)
        font.setWeight(75)
        self.info.setFont(font)
        self.info.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.info.setObjectName("info")
        self.pushButton_2 = QtWidgets.QPushButton(Form)
        self.pushButton_2.setGeometry(QtCore.QRect(0, 560, 113, 32))
        self.pushButton_2.setObjectName("pushButton_2")
        self.lineEdit = QtWidgets.QLineEdit(Form)
        self.lineEdit.setEnabled(True)
        self.lineEdit.setGeometry(QtCore.QRect(120, 565, 561, 21))
        self.lineEdit.setText("")
        self.lineEdit.setObjectName("lineEdit")
        self.lineEdit.setPlaceholderText("Type in the path to your ivi file. ")
        self.pushButton_3 = QtWidgets.QPushButton(Form)
        self.pushButton_3.setGeometry(QtCore.QRect(690, 560, 108, 32))
        self.pushButton_3.setObjectName("pushButton_3")
        self.pushButton_4 = QtWidgets.QPushButton(Form)
        self.pushButton_4.setGeometry(QtCore.QRect(0, 485, 113, 32))
        self.pushButton_4.setObjectName("pushButton_4")
        self.horizontalSlider = QtWidgets.QSlider(Form)
        self.horizontalSlider.setGeometry(QtCore.QRect(120, 490, 571, 22))
        self.horizontalSlider.setOrientation(QtCore.Qt.Orientation.Horizontal)
        self.horizontalSlider.setObjectName("horizontalSlider")
        self.label = QtWidgets.QLabel(Form)
        self.label.setGeometry(QtCore.QRect(699, 449, 81, 31))
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(Form)
        self.label_2.setGeometry(QtCore.QRect(700, 490, 60, 16))
        self.label_2.setObjectName("label_2")

        # Slot & String
        self.pp.setEnabled(False)
        self.pushButton.setEnabled(False)
        self.retranslateUi(Form)
        self.win.closeEvent = self.on_close_check
        self.pushButton_2.clicked.connect(self.open_ivi)
        self.pushButton_3.clicked.connect(self.close_ivi)
        self.pushButton_4.clicked.connect(self.sound_on_off_event)
        self.pp.clicked.connect(self.pp_button)
        self.horizontalSlider.valueChanged.connect(self.volume_change_event)
        self.slider.sliderReleased.connect(self.position_change_event)
        self.slider.sliderPressed.connect(self.position_start_change_event)
        self.pushButton.clicked.connect(self.back_to_previous)

        # InteractiveVariables
        self.current_node = 0
        self.variables: List[InteractiveVariable] = []
        self.state_log = []
        self.graph = None
        self.choice_buttons: List[Button] = []
        self.choice_labels: List[ButtonLabel] = []

        # Video Play Variables & Functions
        self.temp_dir = ""
        self.is_draging_slider = False
        self.is_stoping = False
        self.win.startTimer(100)
        self.has_end = False
        self.final_position = -1

        # Timer & Refresh
        def timerEvent(*args, **kwargs):
            # 创建要跳转的节点
            if self.has_end:
                if len(self.choice_labels) != 0:
                    for lbl in self.choice_labels:
                        lbl.raise_()
                    self.player.lower()
                else:
                    children = self.graph[str(self.current_node)]["sub"]
                    if len(children) == 0:
                        # 已结束
                        pass
                    else:
                        # 跳转类型
                        if (
                            self.graph[str(children[0])]["jump_type"]
                            == InteractiveNodeJumpingType.DEFAULT.value
                        ):
                            # 直接跳转
                            for node_id in children:
                                btn = Button(
                                    node_id,
                                    [0, 0],
                                    "",
                                    self.graph[str(node_id)]["condition"],
                                    self.graph[str(node_id)]["command"],
                                )
                                condition = InteractiveJumpingCondition(
                                    self.variables, btn.condition
                                )
                                if condition.get_result():
                                    # 可以跳转
                                    native_command = InteractiveJumpingCommand(
                                        self.variables, btn.command
                                    )
                                    self.variables = native_command.run_command()
                                    btn_id = btn.node_id
                                    self.set_source(self.graph[str(btn_id)]["cid"])
                                    self.current_node = btn.node_id
                                    self.volume_change_event()
                                    title = self.graph[str(node_id)]["title"]
                                    self.node.setText(f"(当前节点: {title})")
                                    break
                        else:
                            # 进行选择
                            def get_info(node_id: int):
                                return self.graph[str(node_id)]

                            cnt = 0
                            for idx, child in enumerate(children):
                                pos_x = cnt * 200
                                pos_y = 600
                                cur_info = get_info(child)
                                # 生成 Button 对象
                                self.choice_buttons.append(
                                    Button(
                                        child,
                                        [pos_x, pos_y],
                                        cur_info["button"]["text"],
                                        cur_info["condition"],
                                        cur_info["command"],
                                    )
                                )
                                # 生成 ButtonLabel 对象
                                if cur_info["button"]["pos"][0] == None:
                                    if idx != 0:
                                        previous_info = get_info(children[idx - 1])
                                        curtext, previoustext = (
                                            cur_info["button"]["text"],
                                            previous_info["button"]["text"],
                                        )
                                        if curtext[2:] == previoustext[2:]:
                                            # 可确定与上一个按钮同一个位置(即概率按钮)
                                            # 不再生成单独的 label
                                            self.choice_buttons[-1].pos[0] -= 200
                                            continue
                                    cnt += 1
                                    lbl = ButtonLabel(self.win)
                                    lbl.prep_text(
                                        cur_info["button"]["text"], pos_x, pos_y
                                    )
                                    lbl.show()
                                    self.choice_labels.append(lbl)
                                    continue
                                if idx != 0:
                                    previous_info = get_info(children[idx - 1])
                                    curpos, previouspos = (
                                        cur_info["button"]["pos"],
                                        previous_info["button"]["pos"],
                                    )
                                    if (abs(curpos[0] - previouspos[0]) <= 5) and (
                                        abs(curpos[1] - previouspos[1]) <= 5
                                    ):
                                        # 可确定与上一个按钮同一个位置(即概率按钮)
                                        # 不再生成单独的 label
                                        self.choice_buttons[-1].pos[0] -= 200
                                    else:
                                        # 生成 label
                                        cnt += 1
                                        lbl = ButtonLabel(self.win)
                                        lbl.prep_text(
                                            cur_info["button"]["text"], pos_x, pos_y
                                        )
                                        lbl.show()
                                        self.choice_labels.append(lbl)
                                else:
                                    # 生成 label
                                    cnt += 1
                                    lbl = ButtonLabel(self.win)
                                    lbl.prep_text(
                                        cur_info["button"]["text"], pos_x, pos_y
                                    )
                                    lbl.show()
                                    self.choice_labels.append(lbl)
                                    pass
                            add_space = int((800 - cnt * 200) / 2)
                            for idx, lbl in enumerate(self.choice_labels):
                                lbl.setGeometry(
                                    QtCore.QRect(
                                        lbl.geometry().left() + add_space,
                                        lbl.geometry().top(),
                                        lbl.geometry().width(),
                                        lbl.geometry().height(),
                                    )
                                )
                            for btn in self.choice_buttons:
                                btn.pos[0] += add_space
            # 处理进度条
            if self.is_draging_slider:
                return
            if self.mediaplayer.duration() == 0:
                self.slider.setValue(100)
                self.label.setText("--:--/--:--")
                return
            if (
                (self.mediaplayer.duration() // 1000)
                == ((self.mediaplayer.position() // 1000))
            ) and (not self.has_end):
                self.has_end = True
                self.mediaplayer.pause()
                self.final_position = self.mediaplayer.position()
                self.mediaplayer.setAudioOutput(
                    QtMultimedia.QAudioOutput().setVolume(0)
                )
                self.slider.setValue(100)
                duration = self.mediaplayer.duration() // 1000
                duration_sec = duration % 60
                duration_min = duration // 60
                if duration_sec < 10:
                    duration_sec = "0" + str(duration_sec)
                if duration_min < 10:
                    duration_min = "0" + str(duration_min)
                self.label.setText(
                    f"{duration_min}:{duration_sec}/{duration_min}:{duration_sec}"
                )
                self.player.lower()
                for lbl in self.choice_labels:
                    lbl.raise_()
                return
            elif self.has_end:
                self.has_end = True
                self.slider.setValue(100)
                self.mediaplayer.setPosition(self.final_position)
                self.mediaplayer.setAudioOutput(
                    QtMultimedia.QAudioOutput().setVolume(0)
                )
                duration = self.mediaplayer.duration() // 1000
                duration_sec = duration % 60
                duration_min = duration // 60
                if duration_sec < 10:
                    duration_sec = "0" + str(duration_sec)
                if duration_min < 10:
                    duration_min = "0" + str(duration_min)
                self.label.setText(
                    f"{duration_min}:{duration_sec}/{duration_min}:{duration_sec}"
                )
                self.player.lower()
                for lbl in self.choice_labels:
                    lbl.raise_()
                return
            else:
                self.has_end = False
                self.choice_buttons = []
                for lbl in self.choice_labels:
                    lbl.hide()
                self.choice_labels = []
            self.last_position = self.mediaplayer.position()
            self.slider.setValue(
                int(self.mediaplayer.position() / self.mediaplayer.duration() * 100)
            )
            duration = self.mediaplayer.duration() // 1000
            position = self.mediaplayer.position() // 1000
            duration_sec = duration % 60
            duration_min = duration // 60
            position_sec = position % 60
            position_min = position // 60
            if duration_sec < 10:
                duration_sec = "0" + str(duration_sec)
            if duration_min < 10:
                duration_min = "0" + str(duration_min)
            if position_sec < 10:
                position_sec = "0" + str(position_sec)
            if position_min < 10:
                position_min = "0" + str(position_min)
            self.label.setText(
                f"{position_min}:{position_sec}/{duration_min}:{duration_sec}"
            )
            # 将选择的按钮置于最上层
            for lbl in self.choice_labels:
                lbl.raise_()

        self.win.timerEvent = timerEvent

        # Click & Jump
        def mouseReleaseEvent(event: QtGui.QMouseEvent):
            pos = event.position()
            pos = [pos.x(), pos.y()]
            for var in self.variables:
                if var.is_random():
                    var._InteractiveVariable__var_value = random.random() * 100
            for btn in self.choice_buttons:
                if (
                    (pos[0] - btn.pos[0] <= 200)
                    and (pos[0] - btn.pos[0] >= 0)
                    and (pos[1] - btn.pos[1] <= 50)
                    and (pos[1] - btn.pos[1] >= 0)
                ):
                    condition = InteractiveJumpingCondition(
                        self.variables, btn.condition
                    )
                    if condition.get_result():
                        # 可以跳转
                        native_command = InteractiveJumpingCommand(
                            self.variables, btn.command
                        )
                        self.variables = native_command.run_command()
                        btn_id = btn.node_id
                        self.set_source(self.graph[str(btn_id)]["cid"])
                        self.current_node = btn.node_id
                        self.volume_change_event()
                        title = self.graph[str(btn.node_id)]["title"]
                        self.node.setText(f"(当前节点: {title})")
                        break

        self.win.mouseReleaseEvent = mouseReleaseEvent

    def start_playing(self):
        self.mediaplayer.play()
        self.is_stoping = False

    def stop_playing(self):
        self.mediaplayer.stop()
        self.is_stoping = True

    def pause_playing(self):
        self.mediaplayer.pause()
        self.is_stoping = True

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        self.pp.setText(_translate("Form", "Pause"))
        self.pushButton.setText(_translate("Form", "<- Previous"))
        self.node.setText(_translate("Form", "(当前节点: 无)"))
        self.info.setText(_translate("Form", "视频标题(BVID)"))
        self.pushButton_2.setText(_translate("Form", "Open"))
        self.pushButton_3.setText(_translate("Form", "Close"))
        self.pushButton_4.setText(_translate("Form", "Sound: Off"))
        self.label.setText(_translate("Form", "--:--/--:--"))
        self.label_2.setText(_translate("Form", "0"))

    def set_source(self, cid: int):
        wintitle = "MPlayer"
        for var in self.variables:
            if var.is_show():
                wintitle += f" - {var.get_name()}: {int(var.get_value())}"
        for lbl in self.choice_labels:
            lbl.hide()
        self.choice_labels = []
        self.choice_buttons = []
        self.win.setWindowTitle(wintitle)
        self.state_log.append({"cid": cid, "vars": copy.deepcopy(self.variables)})
        self.has_end = False
        self.mediaplayer.setAudioOutput(
            QtMultimedia.QAudioOutput().setVolume(self.horizontalSlider.value() / 100)
        )
        self.stop_playing()
        self.pp.setText("Pause")
        dest = self.temp_dir + str(cid) + ".mp4"
        self.mediaplayer.setSource(QtCore.QUrl(dest))
        self.mediaplayer.setPosition(0)
        if self.mediaplayer.duration() <= 7:
            self.mediaplayer.setPosition(self.mediaplayer.duration())
        self.start_playing()

    def extract_ivi(self, path: str):
        curtime = str(time.time())
        try:
            os.mkdir(".mplayer")
        except:
            pass
        os.mkdir(".mplayer/" + curtime)
        self.temp_dir = ".mplayer/" + curtime + "/"
        ivi = zipfile.ZipFile(path)
        ivi.extractall(self.temp_dir)
        bilivideo_parser = json.JSONDecoder()
        self.node.setText("(当前节点: 视频主节点)")
        self.info.setText(
            bilivideo_parser.decode(
                open(self.temp_dir + "bilivideo.json", "r", encoding="utf-8").read()
            )["title"]
            + "("
            + bilivideo_parser.decode(
                open(self.temp_dir + "bilivideo.json", "r", encoding="utf-8").read()
            )["bvid"]
            + ")"
        )
        self.graph = json.load(
            open(self.temp_dir + "ivideo.json", "r", encoding="utf-8")
        )
        self.current_node = 1
        variables = self.graph["1"]["vars"]
        for var in variables:
            self.variables.append(
                InteractiveVariable(
                    var["name"], var["id"], var["value"], var["show"], var["random"]
                )
            )
        self.set_source(self.graph["1"]["cid"])
        self.volume_change_event()

    def close_ivi(self):
        self.player.hide()
        self.player = QtMultimediaWidgets.QVideoWidget(self.win)
        self.player.setGeometry(QtCore.QRect(0, 0, 800, 450))
        self.player.setObjectName("player")
        self.player.show()
        self.current_node = 0
        self.variables = []
        self.state_log = []
        self.choice_buttons = []
        for lbl in self.choice_labels:
            lbl.hide()
        self.choice_labels = []
        self.graph = None
        self.stop_playing()
        self.pp.setText("Pause")
        self.has_end = False
        self.mediaplayer = QtMultimedia.QMediaPlayer()  # Clear the multimedia source
        self.mediaplayer.setVideoOutput(self.player)
        self.mediaplayer.setAudioOutput(QtMultimedia.QAudioOutput())
        self.volume_change_event()
        shutil.rmtree(self.temp_dir)
        while True:
            if not os.path.exists(self.temp_dir):
                break
        try:
            os.rmdir(".mplayer")
        except:
            # Not empty
            pass
        self.temp_dir = ""
        self.node.setText("(当前节点: 无)")
        self.info.setText("视频标题(BVID)")
        self.win.setWindowTitle("MPlayer")
        self.lineEdit.setText("")
        self.pp.setEnabled(False)
        self.pushButton.setEnabled(False)

    def open_ivi(self):
        if self.current_node != 0:
            return
        try:
            if self.lineEdit.text() != "":
                self.extract_ivi(self.lineEdit.text())
            else:
                dialog = QtWidgets.QFileDialog()
                filename, _ = dialog.getOpenFileName(
                    self.win,
                    "Choose an 'ivi' file to open. ",
                    filter="Bilibili Interactive Video (*.ivi)",
                )
                self.extract_ivi(filename)
                self.lineEdit.setText(filename)
            self.pp.setEnabled(True)
            self.pushButton.setEnabled(True)
        except Exception as e:
            warning = QtWidgets.QMessageBox()
            warning.warning(self.win, "Oops...", str(e))

    def volume_change_event(self):
        if self.horizontalSlider.value() == 0:
            self.pushButton_4.setText("Sound: Off")
        else:
            self.pushButton_4.setText("Sound: On")
        position = self.mediaplayer.position()
        if (not self.has_end) or (not self.is_stoping):
            pass
        else:
            self.pause_playing()
        volume = self.horizontalSlider.value()
        self.label_2.setText(str(volume))
        self.audio_output.setVolume(float(volume / 100))
        self.mediaplayer.setAudioOutput(self.audio_output)
        if (not self.has_end) or (not self.is_stoping):
            pass
        else:
            self.mediaplayer.setPosition(position)
            self.start_playing()

    def position_start_change_event(self):
        self.mediaplayer.pause()
        self.is_draging_slider = True

    def position_change_event(self):
        volume = self.slider.value()
        if volume != 100 and self.has_end:
            self.slider.setValue(100)
            return
        self.mediaplayer.setPosition(int(self.mediaplayer.duration() * volume / 100))
        if not self.is_stoping:
            self.start_playing()
        self.is_draging_slider = False

    def sound_on_off_event(self):
        if "on" in self.pushButton_4.text().lower():
            self.pushButton_4.setText("Sound: Off")
            curpos = self.mediaplayer.position()
            self.mediaplayer.pause()
            volume = self.horizontalSlider.value()
            self.label_2.setText(str(volume))
            self.audio_output = QtMultimedia.QAudioOutput()
            self.audio_output.setVolume(0.0)
            self.mediaplayer.setAudioOutput(self.audio_output)
            self.mediaplayer.setPosition(curpos)
            if not self.is_stoping:
                self.start_playing()
            self.horizontalSlider.setSliderPosition(0)
        else:
            self.pushButton_4.setText("Sound: On")
            curpos = self.mediaplayer.position()
            self.mediaplayer.pause()
            volume = self.horizontalSlider.value()
            self.label_2.setText(str(volume))
            self.audio_output = QtMultimedia.QAudioOutput()
            self.audio_output.setVolume(1.0)
            self.mediaplayer.setAudioOutput(self.audio_output)
            self.mediaplayer.setPosition(curpos)
            if not self.is_stoping:
                self.start_playing()
            self.horizontalSlider.setSliderPosition(100)

    def pp_button(self):
        if self.is_stoping:
            self.start_playing()
            self.pp.setText("Pause")
        else:
            self.pause_playing()
            self.pp.setText("Play")

    def on_close_check(self, event):
        if self.current_node != 0:
            reply = QtWidgets.QMessageBox.question(
                self.win,
                "WARNING",
                "IVI file is playing. Are you sure want to exit? ",
                QtWidgets.QMessageBox.StandardButton.Yes
                | QtWidgets.QMessageBox.StandardButton.No,
                QtWidgets.QMessageBox.StandardButton.No,
            )
            if reply == QtWidgets.QMessageBox.StandardButton.Yes:
                self.close_ivi()
                event.accept()
            else:
                event.ignore()
        else:
            event.accept()

    def back_to_previous(self):
        if len(self.state_log) < 2:
            QtWidgets.QMessageBox.warning(
                self.win,
                "WTF???",
                "MPlayer can't find the previous node. \nMaybe there's not any node or only one node?",
            )
            return
        new_cid = copy.deepcopy(self.state_log[-2]["cid"])
        new_vars = copy.deepcopy(self.state_log[-2]["vars"])
        self.state_log.pop()
        for key in self.graph.keys():
            if self.graph[key]["cid"] == new_cid:
                new_node_id = int(key)
                self.current_node = new_node_id
                self.variables = new_vars
                self.set_source(new_cid)
                self.state_log.pop()
                title = self.graph[str(new_node_id)]["title"]
                self.node.setText(f"(当前节点: {title})")
                self.volume_change_event()
                return


def main():
    app = QtWidgets.QApplication(sys.argv)
    win = QtWidgets.QMainWindow()
    ui = MPlayer()
    ui.setup(win)
    win.show()
    sys.exit(app.exec())


def prepopen(path: str):
    app = QtWidgets.QApplication(sys.argv)
    win = QtWidgets.QMainWindow()
    ui = MPlayer()
    ui.setup(win)
    ui.lineEdit.setText(path)
    ui.open_ivi()
    win.show()
    sys.exit(app.exec())


if __name__ == "__main__":
    main()