from textual.app import App, ComposeResult
from textual.widgets import (
    Header,
    Footer,
    ListView,
    ProgressBar,
    DirectoryTree,
    ListItem,
    Label,
    Static,
    Button,
)
from textual.containers import Container, Horizontal, Center
from textual.screen import Screen
import pathlib
import threading
import edge_tts as tts
from playsound import playsound
import particles as pt
from reactor import Reactor, Apparatus
import auxiliary as aux
import compositions as compo
import builtins

# Hook python 的 open() 函数, 使用 utf-8 (兼容 Windows 万年 GBK)

_original_open = builtins.open

def _open(*args, **kwargs):
    if "encoding" not in kwargs:
        kwargs["encoding"] = "utf-8"
    return _original_open(*args, **kwargs)

builtins.open = _open

ver = "0.3.0"

config = aux.ConfigFile("config.toml")


class MemScreen(Screen):
    BINDINGS = [
        ("d", "toggle_dark", "改变色调"),
        ("q", "pop_screen", "返回主菜单"),
        ("v", "play_voice", "朗读"),
    ]
    if config.get("quick_pass"):
        BINDINGS.append(("k", "quick_pass", "快速通过[调试]"))
    btn = dict()

    def __init__(
        self,
        nucleon_file: pt.NucleonUnion,
        electron_file: pt.ElectronUnion,
        tasked_num,
    ):
        super().__init__(name=None, id=None, classes=None)
        self.reactor = Reactor(nucleon_file, electron_file, self, tasked_num)
        self.stage = 1
        self.stage += self.reactor.set_round_templated(self.stage)
        self.reactor.forward()
        self.compo = next(self.reactor.current_appar)

    def compose(self) -> ComposeResult:
        yield Header(show_clock=True)
        with Center():
            yield Static(
                f"{len(self.reactor.procession) - self.reactor.index}/{len(self.reactor.procession)}"
            )
        yield from self.compo.compose()
        yield Footer()

    def on_mount(self):
        pass

    def on_button_pressed(self, event):
        ret = self.compo.handler(event, "button")
        self._forward_judge(ret)

    def _forward_judge(self, ret):
        if ret == -1:
            return
        if ret == 0:
            try:
                self.compo = next(self.reactor.current_appar)
                self.refresh_ui()
            except StopIteration:
                nxt = self.reactor.forward(1)
                try:
                    self.compo = next(self.reactor.current_appar)
                except:
                    pass
                if nxt == -1:
                    if self.reactor.round_set == 0:
                        if self.stage == 4:
                            if config.get("save"):
                                self.reactor.save()
                            self.compo = compo.Finished(
                                self, None, pt.Atom.placeholder()
                            )
                            self.refresh_ui()
                        else:
                            self.reactor.set_round_templated(self.stage)
                            self.reactor.forward(1)
                            self.stage += 1
                            self.compo = next(self.reactor.current_appar)
                            self.refresh_ui()
                        return
                    return
                else:
                    self.refresh_ui()
                    return
        if ret == 1:
            self.refresh_ui()
            return

    def refresh_ui(self):
        self.call_later(self.recompose)

    def action_play_voice(self):
        def play():
            cache_dir = pathlib.Path(f"./cache/voice/")
            cache_dir.mkdir(parents=True, exist_ok=True)
            cache = cache_dir / f"{self.reactor.current_atom[1].content.replace('/','')}.wav"
            if not cache.exists():
                communicate = tts.Communicate(
                    self.reactor.current_atom[1].content.replace("/", ""),
                    "zh-CN-YunjianNeural",
                )
                communicate.save_sync(
                    f"./cache/voice/{self.reactor.current_atom[1].content.replace('/','')}.wav"
                )
            playsound(str(cache))

        threading.Thread(target=play).start()

    def action_quick_pass(self):
        self.reactor.report(self.reactor.current_atom, 5)
        self._forward_judge(0)
    def action_toggle_dark(self):
        self.app.action_toggle_dark()

    def action_pop_screen(self):
        self.app.pop_screen()


class PreparationScreen(Screen):
    BINDINGS = [("q", "go_back", "返回"), ("escape", "quit_app", "退出")]

    def __init__(
        self, nucleon_file: pt.NucleonUnion, electron_file: pt.ElectronUnion
    ) -> None:
        super().__init__(name=None, id=None, classes=None)
        self.nucleon_file = nucleon_file
        self.electron_file = electron_file

    def compose(self) -> ComposeResult:
        yield Header(show_clock=True)
        with Container(id="learning_screen_container"):
            yield Label(f"记忆项目: [b]{self.nucleon_file.name}[/b]\n")
            yield Label(
                f"核子文件对象: ./nucleon/[b]{self.nucleon_file.name}[/b].toml"
            )
            yield Label(
                f"电子文件对象: ./electron/[b]{self.electron_file.name}[/b].toml"
            )
            yield Label(f"核子数量:{len(self.nucleon_file)}")
            yield Button(
                "开始记忆",
                id="start_memorizing_button",
                variant="primary",
                classes="start-button",
            )
            yield Static(f"\n全文如下:\n")
            yield Static(self._get_full_content().replace("/", ""), classes="full")
        yield Footer()

    def _get_full_content(self):
        content = ""
        for i in self.nucleon_file.nucleons:
            content += i["content"]
        return content

    def action_go_back(self):
        self.app.pop_screen()

    def action_quit_app(self):
        self.app.exit()

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "start_memorizing_button":
            newscr = MemScreen(
                self.nucleon_file, self.electron_file, config.get("tasked_number", 8)
            )
            self.app.push_screen(newscr)


class FileSelectorScreen(Screen):
    global ver

    def compose(self) -> ComposeResult:
        yield Header(show_clock=True)
        yield Container(
            Label(f'欢迎使用 "潜进" 辅助记忆软件, 版本 {ver}', classes="title-label"),
            Label("选择要学习的文件:", classes="title-label"),
            ListView(id="file-list", classes="file-list-view"),
        )
        yield Footer()

    def on_mount(self) -> None:
        file_list_widget = self.query_one("#file-list", ListView)
        nucleon_path = pathlib.Path("./nucleon")
        nucleon_files = sorted(
            [f.name for f in nucleon_path.iterdir() if f.suffix == ".toml"]
        )

        if nucleon_files:
            for filename in nucleon_files:
                file_list_widget.append(ListItem(Label(filename)))
        else:
            file_list_widget.append(
                ListItem(Static("在 ./nucleon/ 中未找到任何核子文件. 请放置文件后重启应用."))
            )
            file_list_widget.disabled = True

    def on_list_view_selected(self, event: ListView.Selected) -> None:
        if not isinstance(event.item, ListItem):
            return

        selected_label = event.item.query_one(Label)
        if "未找到任何 .toml 文件" in str(selected_label.renderable):
            return

        selected_filename = str(selected_label.renderable)
        nucleon_file = pt.NucleonUnion(
            pathlib.Path("./nucleon") / selected_filename
        )
        electron_file_path = pathlib.Path("./electron") / selected_filename
        if electron_file_path.exists():
            pass
        else:
            electron_file_path.touch()
        electron_file = pt.ElectronUnion(
            pathlib.Path("./electron") / selected_filename
        )
        self.app.push_screen(PreparationScreen(nucleon_file, electron_file))

    def action_quit_app(self) -> None:
        self.app.exit()


class AppLauncher(App):
    CSS_PATH = "styles.tcss"
    TITLE = "潜进 - 辅助记忆程序"
    BINDINGS = [("escape", "quit", "退出"), ("d", "toggle_dark", "改变色调")]
    SCREENS = {
        "file_selection_screen": FileSelectorScreen,
    }

    def on_mount(self) -> None:
        self.push_screen("file_selection_screen")


if __name__ == "__main__":
    app = AppLauncher()
    app.run()