"""
「はい」か「いいえ」の選択メッセージを表示するための Kivy 用ダイアログボックス

    Last modified: 2025/06/24

    Copyright (c) 2025 toshifumi tsutsui
    Released under the MIT license
    https://wpandora8.net/the_mit_license.html
"""

from typing import Callable

import kivy.resources
import my_dialogs.helper as helper
from kivy.core.window import Keyboard, Window
from kivy.lang import Builder
from kivy.metrics import dp
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.modalview import ModalView

kivy.resources.resource_add_path(helper.get_resource_path("my_dialogs/images"))

Builder.load_string("""
<_YesNoBoxLayout>:
    size_hint: 0.8, 0.8
    BoxLayout:
        orientation: 'vertical'
        BoxLayout:
            size_hint_y: 1.0-((button_no.height+dp(4))/(root.height*root.size_hint_y))
            orientation: 'horizontal'
            Image:
                size_hint_x: 0.2
                source: 'question_128.png'
                fit_mode: 'contain'
            BoxLayout:
                size_hint_x: 0.1
            Label:
                size_hint_x: 0.7
                text_size: self.size
                valign: 'middle'
                text: root.message
                line_height: 1.5
        AnchorLayout:
            size_hint_y: (button_no.height+dp(4))/(root.height*root.size_hint_y)
            BoxLayout:
                spacing: dp(20)
                orientation: 'horizontal'
                Button:
                    id: button_no
                    size_hint_y: None
                    height: dp(48)
                    text: "いいえ"
                    on_release: root.button_no_click()
                Button:
                    size_hint_y: None
                    height: dp(48)
                    text: "はい"
                    on_release: root.button_yes_click()
""")


class YesNoBox:
    """「はい」か「いいえ」を選択するメッセージボックスを定義したクラス。"""

    def __init__(
        self,
        message: str = "",
        on_yes_callback: Callable | None = None,
        on_no_callback: Callable | None = None,
        size_hint: tuple[float | None, float | None] = (0.8, 0.5),
        size: tuple[float, float] = (dp(640), dp(480)),
    ) -> None:
        """「はい」か「いいえ」を選択するメッセージボックスのインスタンスを作成して返す。

        Args:
            message (str, optional): 表示するメッセージ。
            on_yes_callbac (Callable | None, optional): 「はい」が選択されたときの処理。
            on_no_callback (Callable | None, optional): 「いいえ」が選択されたときの処理。
            size_hint (tuple[float | None, float | None]): 親ウィンドウに対するダイアログボックスのサイズの比率。
            size (tuple[float, float]): ダイアログボックスのサイズ（size_hint にはそれぞれ None を指定）。
        """

        self._on_yes_callback: Callable | None = on_yes_callback
        self._on_no_callback: Callable | None = on_no_callback

        self._view: ModalView = ModalView(
            size_hint=size_hint, size=size, auto_dismiss=False
        )
        self._view.add_widget(
            _YesNoBoxLayout(
                message=message, button_yes_click=self._yes, button_no_click=self._no
            )
        )

    def show(self) -> None:
        """YesNoBox を表示する。"""

        self._view.open()

    def _yes(self) -> None:
        """「はい」ボタンがクリックされたとき。"""

        self._view.dismiss()

        if self._on_yes_callback is not None:
            self._on_yes_callback()

    def _no(self) -> None:
        """「いいえ」ボタンがクリックされたとき。"""

        self._view.dismiss()

        if self._on_no_callback is not None:
            self._on_no_callback()


class _YesNoBoxLayout(AnchorLayout):
    """「はい」か「いいえ」を選択するメッセージボックスのレイアウト。"""

    button_yes_click = ObjectProperty(None)
    button_no_click = ObjectProperty(None)
    message = StringProperty("")

    def __init__(self, **kwargs) -> None:
        super().__init__(**kwargs)

        self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
        self._keyboard.bind(on_key_down=self._on_keyboard_down)

    def _keyboard_closed(self):
        """キーボードが閉じられたときに呼び出されるコールバック。"""

        self._keyboard.unbind(on_key_down=self._on_keyboard_down)

    def _on_keyboard_down(
        self,
        keyboard: Keyboard,
        keycode: tuple[int, str],
        text: str,
        modifiers: list[str],
    ) -> None:
        """押下されたアクセスキーにより処理を行う。

        Args:
            keyboard (Keyboard): バインドされた Keyboard のインスタンス。
            keycode (tupl[int, str]): 押されたキーのキーコードと文字の tuple。
            text (str): 押されたキーのテキスト。
            modifiers (list[str]): 同時に押された補助キー名の list。
        """

        key: str = keycode[1].lower()

        if key in ("y", "enter"):
            self._keyboard.unbind(on_key_down=self._on_keyboard_down)
            self.button_yes_click()
        elif key in ("n", "escape"):
            self._keyboard.unbind(on_key_down=self._on_keyboard_down)
            self.button_no_click()
