"""
メイン画面を定義したクラス。

    「ちょこっと色見本」
    Copyright (c) 2025 toshifumi tsutsui
    Released under the MIT license
    https://wpandora8.net/the_mit_license.html
"""

from typing import Callable

from kivy.clock import Clock
from kivy.core.clipboard import Clipboard
from kivy.core.window import Window
from kivy.graphics import Color
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.uix.textinput import TextInput
from kivy.uix.widget import Widget

from controller import (
    get_color_code_value,
    get_color_names,
    get_rgb_from_color_code,
    normalize_rgb,
)

Builder.load_string("""
<MainScreen>:
    BoxLayout:
        orientation: 'horizontal'
        size: root.size
        padding: sp(6)
        spacing: sp(4)
        Button:
            id: foreground_button
            text: '最前面'
            on_press: root._on_foreground_button_press()
            size_hint_x: 0.09
        AnchorLayout:
            id: color_label
            size_hint_x: 0.15
            canvas.before:
                Color:
                    rgb: 1.0, 1.0, 1.0
                Rectangle:
                    pos: self.pos
                    size: self.size
        Label:
            size_hint_x: 0.03
            text: 'R:'
            text_size: self.size
            halign: 'right'
            valign: 'middle'
        TextInput:
            id: r_textinput
            text: '255'
            size_hint_x: 0.05
            padding: sp(6), sp(10)
            multiline: False
            write_tab: False
            text_validate_unfocus: True
            on_text_validate: root._move_focus(self)
            on_focus: root._on_focus(self)
        Label:
            size_hint_x: 0.03
            text: 'G:'
            text_size: self.size
            halign: 'right'
            valign: 'middle'
        TextInput:
            id: g_textinput
            text: '255'
            size_hint_x: 0.05
            padding: sp(6), sp(10)
            multiline: False
            write_tab: False
            text_validate_unfocus: True
            on_text_validate: root._move_focus(self)
            on_focus: root._on_focus(self)
        Label:
            size_hint_x: 0.03
            text: 'B:'
            text_size: self.size
            halign: 'right'
            valign: 'middle'
        TextInput:
            id: b_textinput
            text: '255'
            size_hint_x: 0.05
            padding: sp(6), sp(10)
            multiline: False
            write_tab: False
            text_validate_unfocus: True
            on_text_validate: root._move_focus(self)
            on_focus: root._on_focus(self)
        Button:
            id: color_format_button
            text: 'RGB'
            size_hint_x: 0.09
            on_press: root._on_color_format_button_press()
        TextInput:
            id: color_code_textinput
            text: '255, 255, 255'
            size_hint_x: 0.19
            padding: sp(6), sp(10)
            color: 0, 0, 0, 1
            multiline: False
            write_tab: False
            text_validate_unfocus: True
            on_text_validate: root._move_focus(self)
            on_focus: root._on_focus(self)
        Button:
            id: copy_button
            text: 'コピー'
            size_hint_x: 0.09
            on_press: root._on_copy_button_press()
        Button:
            id: paste_button
            text: '貼り付け'
            size_hint_x: 0.09
            on_press: root._on_paste_button_press()
        ImageButton:
            size_hint_x: 0.06
            on_press: root._on_version_info_button_press()

<ImageButton@ButtonBehavior+Image>:
    source: 'image_64.png'
    fit_mode: 'contain'
""")


class MainScreen(Screen):
    """メイン画面を定義したクラス。"""

    def __init__(self, to_version_screen: Callable[[], None], **kwargs) -> None:
        """
        メイン画面のインスタンスを作成して返す。

        Args:
            to_version_screen (Callable[[], None]): バージョン情報画面に遷移するための関数。
        """
        super().__init__(**kwargs)

        self._to_version_screen: Callable[[], None] = to_version_screen
        """バージョン情報画面に遷移するためのコールバック関数"""

        self._color_names: dict[str, str] = get_color_names()
        """色名と 16 進数表記の辞書"""

        self._rgb: list[int] = [255, 255, 255]
        """現在の色（RGB）"""

        self._color_sample: Color = self.ids.color_label.canvas.before.children[0]
        """色見本の Color 命令オブジェクト"""

        self._color_sample.rgb = normalize_rgb(self._rgb)

        self._color_formats: list[str] = [
            "HEX",
            "RGB",
            "RGB\nNormal",
            "HLS",
            "HLS\nNormal",
            "HSV",
            "HSV\nNormal",
            "Name",
        ]
        """カラーフォーマットのリスト"""

        self.ids.color_format_button.text = self._color_formats[0]
        self.ids.color_code_textinput.text = get_color_code_value(
            self.ids.color_format_button.text, self._rgb, self._color_names
        )

        Window.bind(mouse_pos=self._on_mouse_pos)

        if Window.always_on_top:
            self.ids.foreground_button.text = "✓最前面"
        else:
            self.ids.foreground_button.text = "最前面"

    def _on_mouse_pos(self, instance: Widget, pos: tuple[float, float]) -> None:
        """
        マウスの位置が変化したときの処理。

        Args:
            instance (Widget): マウス位置を監視しているウィジェット。
            pos (tuple[float, float]): マウスの新しい位置。
        """

        if not self.get_root_window():
            return

        if self.ids.r_textinput.collide_point(*pos):
            Window.set_system_cursor("ibeam")
        elif self.ids.g_textinput.collide_point(*pos):
            Window.set_system_cursor("ibeam")
        elif self.ids.b_textinput.collide_point(*pos):
            Window.set_system_cursor("ibeam")
        elif self.ids.color_code_textinput.collide_point(*pos):
            Window.set_system_cursor("ibeam")
        else:
            Window.set_system_cursor("arrow")

    def _on_foreground_button_press(self) -> None:
        """「最前面」ボタンがクリックされたときの処理。"""

        if Window.always_on_top:
            Window.always_on_top = False
            self.ids.foreground_button.text = "最前面"
        else:
            Window.always_on_top = True
            self.ids.foreground_button.text = "✓最前面"

    def _move_focus(self, instance: TextInput) -> None:
        """
        TextInput で Enter キーが押されたら、フォーカスを次の TextInput に移動する。

        Args:
            instance (TextInput): Enter キーが押された TextInput インスタンス。
        """

        if instance is self.ids.r_textinput:
            self.ids.g_textinput.focus = True
        elif instance is self.ids.g_textinput:
            self.ids.b_textinput.focus = True
        elif instance is self.ids.b_textinput:
            self.ids.color_code_textinput.focus = True
        elif instance is self.ids.color_code_textinput:
            self.ids.r_textinput.focus = True

    def _on_rgb_textinput_change(self) -> None:
        """RGB 値の TextInput が変更されたときの処理。"""

        rgb: list[int] = []
        textinputs: list[TextInput] = [
            self.ids.r_textinput,
            self.ids.g_textinput,
            self.ids.b_textinput,
        ]
        for ti in textinputs:
            try:
                int_value: int = int(ti.text)
                if int_value < 0 or int_value > 255:
                    Clock.schedule_once(lambda dt: self._focus_invalid_textinput(ti), 0)
                    return
            except ValueError:
                Clock.schedule_once(lambda dt: self._focus_invalid_textinput(ti), 0)
                return

            rgb.append(int_value)

        if self._rgb != rgb:
            self._rgb = rgb
            self._color_sample.rgb = normalize_rgb(self._rgb)
            color_format: str = self.ids.color_format_button.text
            self.ids.color_code_textinput.text = get_color_code_value(
                color_format, self._rgb, self._color_names
            )

    def _focus_invalid_textinput(self, instance: TextInput) -> None:
        """
        入力がエラーになった TextInput にフォーカスを当てる。

        Args:
            instance (TextInput): フォーカスを当てる TextInput インスタンス。
        """

        instance.focus = True
        instance.background_color = (1.0, 0.7, 0.7, 1.0)

        if instance is not self.ids.color_code_textinput:
            instance.select_all()

    def _on_focus(self, instance: TextInput) -> None:
        """
        TextInput のフォーカスの状態が変更されたときの処理。

        Args:
            instance (TextInput): フォーカスの状態が変更された TextInput インスタンス。
        """

        if instance.focus:
            instance.background_color = (1.0, 1.0, 0.7, 1.0)
            if instance is not self.ids.color_code_textinput:
                instance.select_all()
        else:
            instance.background_color = (1.0, 1.0, 1.0, 1.0)
            instance.cancel_selection()

            if instance is not self.ids.color_code_textinput:
                self._on_rgb_textinput_change()
            else:
                self._on_color_code_textinput_change()

    def _on_color_format_button_press(self) -> None:
        """カラーフォーマットの切り替えボタンがクリックされたときの処理。"""

        current_index: int = self._color_formats.index(
            self.ids.color_format_button.text
        )
        next_index: int = (current_index + 1) % len(self._color_formats)
        self.ids.color_format_button.text = self._color_formats[next_index]

        self.ids.color_code_textinput.text = get_color_code_value(
            self.ids.color_format_button.text, self._rgb, self._color_names
        )

    def _on_color_code_textinput_change(self) -> None:
        """TextInput のカラーコードが変更されたときの処理。"""

        textinput: TextInput = self.ids.color_code_textinput
        color_format: str = self.ids.color_format_button.text
        rgb: list[int] | None = get_rgb_from_color_code(
            textinput.text, color_format, self._color_names
        )
        if rgb is None:
            Clock.schedule_once(lambda dt: self._focus_invalid_textinput(textinput), 0)
            return

        self._rgb = rgb
        self._color_sample.rgb = normalize_rgb(self._rgb)
        self.ids.r_textinput.text = str(self._rgb[0])
        self.ids.g_textinput.text = str(self._rgb[1])
        self.ids.b_textinput.text = str(self._rgb[2])

    def _on_copy_button_press(self) -> None:
        """「コピー」ボタンがクリックされたときの処理。"""

        Clipboard.copy(self.ids.color_code_textinput.text)

    def _on_paste_button_press(self) -> None:
        """「貼り付け」ボタンがクリックされたときの処理。"""

        pasted_text: str = Clipboard.paste()
        self.ids.color_code_textinput.text = pasted_text
        self._on_color_code_textinput_change()

    def _on_version_info_button_press(self) -> None:
        """「バージョン情報」ボタンがクリックされたときの処理。"""

        self._to_version_screen()
