# Released under the MIT License. See LICENSE for details.
#
"""UI functionality related to advanced gamepad configuring."""

from __future__ import annotations

from typing import TYPE_CHECKING

import bauiv1 as bui
from bauiv1 import builtinassets

if TYPE_CHECKING:
    from typing import Any
    from bauiv1lib.settings.gamepad import (
        GamepadSettingsWindow,
        AwaitGamepadInputWindow,
    )


class GamepadAdvancedSettingsWindow(bui.Window):
    """Window for advanced gamepad configuration."""

    def __init__(self, parent_window: GamepadSettingsWindow):
        # pylint: disable=too-many-statements
        self._parent_window = parent_window

        app = bui.app

        self._r = parent_window.get_r()
        assert bui.app.classic is not None
        uiscale = bui.app.ui_v1.uiscale
        self._width = 900 if uiscale is bui.UIScale.SMALL else 700
        self._x_inset = x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
        self._height = 402 if uiscale is bui.UIScale.SMALL else 512
        self._textwidgets: dict[str, bui.Widget] = {}
        advb = parent_window.get_advanced_button()
        super().__init__(
            root_widget=bui.containerwidget(
                transition='in_scale',
                size=(self._width, self._height),
                scale=1.06
                * (
                    1.6
                    if uiscale is bui.UIScale.SMALL
                    else 1.35 if uiscale is bui.UIScale.MEDIUM else 1.0
                ),
                stack_offset=(
                    (0, -25) if uiscale is bui.UIScale.SMALL else (0, 0)
                ),
                scale_origin_stack_offset=(advb.get_screen_space_center()),
            )
        )

        bui.textwidget(
            parent=self._root_widget,
            position=(
                self._width * 0.5,
                self._height - (40 if uiscale is bui.UIScale.SMALL else 34),
            ),
            size=(0, 0),
            text=bui.Lstr(resource=f'{self._r}.advancedTitleText'),
            maxwidth=320,
            color=bui.app.ui_v1.title_color,
            h_align='center',
            v_align='center',
        )

        back_button = btn = bui.buttonwidget(
            parent=self._root_widget,
            autoselect=True,
            position=(
                self._width - (176 + x_inset),
                self._height - (60 if uiscale is bui.UIScale.SMALL else 55),
            ),
            size=(120, 48),
            text_scale=0.8,
            label=bui.Lstr(resource='doneText'),
            on_activate_call=self._done,
        )
        bui.containerwidget(
            edit=self._root_widget,
            start_button=btn,
            on_cancel_call=btn.activate,
        )

        self._scroll_width = self._width - (100 + 2 * x_inset)
        self._scroll_height = self._height - 110
        self._sub_width = self._scroll_width - 20
        self._sub_height = (
            940 if self._parent_window.get_is_secondary() else 1040
        )
        if app.env.vr:
            self._sub_height += 50
        self._scrollwidget = bui.scrollwidget(
            parent=self._root_widget,
            position=(
                (self._width - self._scroll_width) * 0.5,
                self._height - 65 - self._scroll_height,
            ),
            size=(self._scroll_width, self._scroll_height),
            claims_left_right=True,
            selection_loops_to_parent=True,
        )
        self._subcontainer = bui.containerwidget(
            parent=self._scrollwidget,
            size=(self._sub_width, self._sub_height),
            background=False,
            claims_left_right=True,
            selection_loops_to_parent=True,
        )
        bui.containerwidget(
            edit=self._root_widget, selected_child=self._scrollwidget
        )

        h = 30
        v = self._sub_height - 10

        h2 = h + 12

        # don't allow secondary joysticks to handle unassigned buttons
        if not self._parent_window.get_is_secondary():
            v -= 40
            cb1 = bui.checkboxwidget(
                parent=self._subcontainer,
                position=(h + 70, v),
                size=(500, 30),
                text=bui.Lstr(resource=f'{self._r}.unassignedButtonsRunText'),
                textcolor=(0.8, 0.8, 0.8),
                maxwidth=330,
                scale=1.0,
                on_value_change_call=(
                    self._parent_window.set_unassigned_buttons_run_value
                ),
                autoselect=True,
                value=self._parent_window.get_unassigned_buttons_run_value(),
            )
            bui.widget(edit=cb1, up_widget=back_button)
        v -= 60
        capb = self._capture_button(
            pos=(h2, v),
            name=bui.Lstr(resource=f'{self._r}.runButton1Text'),
            control='buttonRun1' + self._parent_window.get_ext(),
        )
        if self._parent_window.get_is_secondary():
            for widget in capb:
                bui.widget(edit=widget, up_widget=back_button)
        v -= 42
        self._capture_button(
            pos=(h2, v),
            name=bui.Lstr(resource=f'{self._r}.runButton2Text'),
            control='buttonRun2' + self._parent_window.get_ext(),
        )
        bui.textwidget(
            parent=self._subcontainer,
            position=(self._sub_width * 0.5, v - 24),
            size=(0, 0),
            text=bui.Lstr(resource=f'{self._r}.runTriggerDescriptionText'),
            color=(0.7, 1, 0.7, 0.6),
            maxwidth=self._sub_width * 0.8,
            scale=0.7,
            h_align='center',
            v_align='center',
        )

        v -= 85

        self._capture_button(
            pos=(h2, v),
            name=bui.Lstr(resource=f'{self._r}.runTrigger1Text'),
            control='triggerRun1' + self._parent_window.get_ext(),
            message=bui.Lstr(resource=f'{self._r}.pressAnyAnalogTriggerText'),
        )
        v -= 42
        self._capture_button(
            pos=(h2, v),
            name=bui.Lstr(resource=f'{self._r}.runTrigger2Text'),
            control='triggerRun2' + self._parent_window.get_ext(),
            message=bui.Lstr(resource=f'{self._r}.pressAnyAnalogTriggerText'),
        )

        # in vr mode, allow assigning a reset-view button
        if app.env.vr:
            v -= 50
            self._capture_button(
                pos=(h2, v),
                name=bui.Lstr(resource=f'{self._r}.vrReorientButtonText'),
                control='buttonVRReorient' + self._parent_window.get_ext(),
            )

        v -= 60
        self._capture_button(
            pos=(h2, v),
            name=bui.Lstr(resource=f'{self._r}.extraStartButtonText'),
            control='buttonStart2' + self._parent_window.get_ext(),
        )
        v -= 60
        self._capture_button(
            pos=(h2, v),
            name=bui.Lstr(resource=f'{self._r}.ignoredButton1Text'),
            control='buttonIgnored' + self._parent_window.get_ext(),
        )
        v -= 42
        self._capture_button(
            pos=(h2, v),
            name=bui.Lstr(resource=f'{self._r}.ignoredButton2Text'),
            control='buttonIgnored2' + self._parent_window.get_ext(),
        )
        v -= 42
        self._capture_button(
            pos=(h2, v),
            name=bui.Lstr(resource=f'{self._r}.ignoredButton3Text'),
            control='buttonIgnored3' + self._parent_window.get_ext(),
        )
        v -= 42
        self._capture_button(
            pos=(h2, v),
            name=bui.Lstr(resource=f'{self._r}.ignoredButton4Text'),
            control='buttonIgnored4' + self._parent_window.get_ext(),
        )
        bui.textwidget(
            parent=self._subcontainer,
            position=(self._sub_width * 0.5, v - 14),
            size=(0, 0),
            text=bui.Lstr(resource=f'{self._r}.ignoredButtonDescriptionText'),
            color=(0.7, 1, 0.7, 0.6),
            scale=0.8,
            maxwidth=self._sub_width * 0.8,
            h_align='center',
            v_align='center',
        )

        v -= 80
        pwin = self._parent_window
        bui.checkboxwidget(
            parent=self._subcontainer,
            autoselect=True,
            position=(h + 50, v),
            size=(400, 30),
            text=bui.Lstr(
                resource=f'{self._r}.startButtonActivatesDefaultText'
            ),
            textcolor=(0.8, 0.8, 0.8),
            maxwidth=450,
            scale=0.9,
            on_value_change_call=(
                pwin.set_start_button_activates_default_widget_value
            ),
            value=pwin.get_start_button_activates_default_widget_value(),
        )
        bui.textwidget(
            parent=self._subcontainer,
            position=(self._sub_width * 0.5, v - 12),
            size=(0, 0),
            text=bui.Lstr(
                resource=f'{self._r}.startButtonActivatesDefaultDescriptionText'
            ),
            color=(0.7, 1, 0.7, 0.6),
            maxwidth=self._sub_width * 0.8,
            scale=0.7,
            h_align='center',
            v_align='center',
        )

        v -= 80
        bui.checkboxwidget(
            parent=self._subcontainer,
            autoselect=True,
            position=(h + 50, v),
            size=(400, 30),
            text=bui.Lstr(resource=f'{self._r}.uiOnlyText'),
            textcolor=(0.8, 0.8, 0.8),
            maxwidth=450,
            scale=0.9,
            on_value_change_call=self._parent_window.set_ui_only_value,
            value=self._parent_window.get_ui_only_value(),
        )
        bui.textwidget(
            parent=self._subcontainer,
            position=(self._sub_width * 0.5, v - 12),
            size=(0, 0),
            text=bui.Lstr(resource=f'{self._r}.uiOnlyDescriptionText'),
            color=(0.7, 1, 0.7, 0.6),
            maxwidth=self._sub_width * 0.8,
            scale=0.7,
            h_align='center',
            v_align='center',
        )

        v -= 80
        bui.checkboxwidget(
            parent=self._subcontainer,
            autoselect=True,
            position=(h + 50, v),
            size=(400, 30),
            text=bui.Lstr(resource=f'{self._r}.ignoreCompletelyText'),
            textcolor=(0.8, 0.8, 0.8),
            maxwidth=450,
            scale=0.9,
            on_value_change_call=pwin.set_ignore_completely_value,
            value=self._parent_window.get_ignore_completely_value(),
        )
        bui.textwidget(
            parent=self._subcontainer,
            position=(self._sub_width * 0.5, v - 12),
            size=(0, 0),
            text=bui.Lstr(
                resource=f'{self._r}.ignoreCompletelyDescriptionText'
            ),
            color=(0.7, 1, 0.7, 0.6),
            maxwidth=self._sub_width * 0.8,
            scale=0.7,
            h_align='center',
            v_align='center',
        )

        v -= 80

        cb1 = bui.checkboxwidget(
            parent=self._subcontainer,
            autoselect=True,
            position=(h + 50, v),
            size=(400, 30),
            text=bui.Lstr(resource=f'{self._r}.autoRecalibrateText'),
            textcolor=(0.8, 0.8, 0.8),
            maxwidth=450,
            scale=0.9,
            on_value_change_call=pwin.set_auto_recalibrate_analog_stick_value,
            value=self._parent_window.get_auto_recalibrate_analog_stick_value(),
        )
        bui.textwidget(
            parent=self._subcontainer,
            position=(self._sub_width * 0.5, v - 12),
            size=(0, 0),
            text=bui.Lstr(resource=f'{self._r}.autoRecalibrateDescriptionText'),
            color=(0.7, 1, 0.7, 0.6),
            maxwidth=self._sub_width * 0.8,
            scale=0.7,
            h_align='center',
            v_align='center',
        )
        v -= 80

        buttons = self._config_value_editor(
            bui.Lstr(resource=f'{self._r}.analogStickDeadZoneText'),
            control=('analogStickDeadZone' + self._parent_window.get_ext()),
            position=(h + 40, v),
            min_val=0,
            max_val=10.0,
            increment=0.1,
            x_offset=100,
        )
        bui.widget(edit=buttons[0], left_widget=cb1, up_widget=cb1)
        bui.widget(edit=cb1, right_widget=buttons[0], down_widget=buttons[0])

        bui.textwidget(
            parent=self._subcontainer,
            position=(self._sub_width * 0.5, v - 12),
            size=(0, 0),
            text=bui.Lstr(
                resource=f'{self._r}.analogStickDeadZoneDescriptionText'
            ),
            color=(0.7, 1, 0.7, 0.6),
            maxwidth=self._sub_width * 0.8,
            scale=0.7,
            h_align='center',
            v_align='center',
        )
        v -= 100

        # child joysticks cant have child joysticks.. that's just
        # crazy talk
        if not self._parent_window.get_is_secondary():
            bui.buttonwidget(
                parent=self._subcontainer,
                autoselect=True,
                label=bui.Lstr(resource=f'{self._r}.twoInOneSetupText'),
                position=(40, v),
                size=(self._sub_width - 80, 50),
                on_activate_call=self._parent_window.show_secondary_editor,
                up_widget=buttons[0],
            )

        # set a bigger bottom show-buffer for the widgets we just made
        # so we can see the text below them when navigating with
        # a gamepad
        for child in self._subcontainer.get_children():
            bui.widget(edit=child, show_buffer_bottom=30, show_buffer_top=30)

    def _capture_button(
        self,
        pos: tuple[float, float],
        name: bui.Lstr,
        control: str,
        message: bui.Lstr | None = None,
    ) -> tuple[bui.Widget, bui.Widget]:
        if message is None:
            message = bui.Lstr(
                resource=self._parent_window.get_r() + '.pressAnyButtonText'
            )
        btn = bui.buttonwidget(
            parent=self._subcontainer,
            autoselect=True,
            position=(pos[0], pos[1]),
            label=name,
            size=(250, 60),
            scale=0.7,
        )
        btn2 = bui.buttonwidget(
            parent=self._subcontainer,
            autoselect=True,
            position=(pos[0] + 400, pos[1] + 2),
            left_widget=btn,
            color=(0.45, 0.4, 0.5),
            textcolor=(0.65, 0.6, 0.7),
            label=bui.Lstr(resource=f'{self._r}.clearText'),
            size=(110, 50),
            scale=0.7,
            on_activate_call=bui.CallStrict(self._clear_control, control),
        )
        bui.widget(edit=btn, right_widget=btn2)

        # make this in a timer so that it shows up on top of all
        # other buttons

        def doit() -> None:
            from bauiv1lib.settings.gamepad import AwaitGamepadInputWindow

            txt = bui.textwidget(
                parent=self._subcontainer,
                position=(pos[0] + 285, pos[1] + 20),
                color=(1, 1, 1, 0.3),
                size=(0, 0),
                h_align='center',
                v_align='center',
                scale=0.7,
                text=self._parent_window.get_control_value_name(control),
                maxwidth=200,
            )
            self._textwidgets[control] = txt
            bui.buttonwidget(
                edit=btn,
                on_activate_call=bui.CallStrict(
                    AwaitGamepadInputWindow,
                    self._parent_window.get_input(),
                    control,
                    self._gamepad_event,
                    message,
                ),
            )

        bui.pushcall(doit)
        return btn, btn2

    def _inc(
        self, control: str, min_val: float, max_val: float, inc: float
    ) -> None:
        val = self._parent_window.get_settings().get(control, 1.0)
        val = min(max_val, max(min_val, val + inc))
        if abs(1.0 - val) < 0.001:
            if control in self._parent_window.get_settings():
                del self._parent_window.get_settings()[control]
        else:
            self._parent_window.get_settings()[control] = round(val, 1)
        bui.textwidget(
            edit=self._textwidgets[control],
            text=self._parent_window.get_control_value_name(control),
        )

    def _config_value_editor(
        self,
        name: bui.Lstr,
        control: str,
        position: tuple[float, float],
        *,
        min_val: float = 0.0,
        max_val: float = 100.0,
        increment: float = 1.0,
        change_sound: bool = True,
        x_offset: float = 0.0,
        displayname: bui.Lstr | None = None,
    ) -> tuple[bui.Widget, bui.Widget]:
        if displayname is None:
            displayname = name
        bui.textwidget(
            parent=self._subcontainer,
            position=position,
            size=(100, 30),
            text=displayname,
            color=(0.8, 0.8, 0.8, 1.0),
            h_align='left',
            v_align='center',
            scale=1.0,
            maxwidth=280,
        )
        self._textwidgets[control] = bui.textwidget(
            parent=self._subcontainer,
            position=(246.0 + x_offset, position[1]),
            size=(60, 28),
            editable=False,
            color=(0.3, 1.0, 0.3, 1.0),
            h_align='right',
            v_align='center',
            text=self._parent_window.get_control_value_name(control),
            padding=2,
        )
        btn = bui.buttonwidget(
            parent=self._subcontainer,
            autoselect=True,
            position=(330 + x_offset, position[1] + 4),
            size=(28, 28),
            label='-',
            on_activate_call=bui.CallStrict(
                self._inc, control, min_val, max_val, -increment
            ),
            repeat=True,
            enable_sound=(change_sound is True),
        )
        btn2 = bui.buttonwidget(
            parent=self._subcontainer,
            autoselect=True,
            position=(380 + x_offset, position[1] + 4),
            size=(28, 28),
            label='+',
            on_activate_call=bui.CallStrict(
                self._inc, control, min_val, max_val, increment
            ),
            repeat=True,
            enable_sound=(change_sound is True),
        )
        return btn, btn2

    def _clear_control(self, control: str) -> None:
        if control in self._parent_window.get_settings():
            del self._parent_window.get_settings()[control]
        bui.textwidget(
            edit=self._textwidgets[control],
            text=self._parent_window.get_control_value_name(control),
        )

    def _gamepad_event(
        self,
        control: str,
        event: dict[str, Any],
        dialog: AwaitGamepadInputWindow,
    ) -> None:
        ext = self._parent_window.get_ext()
        if control in ['triggerRun1' + ext, 'triggerRun2' + ext]:
            if event['type'] == 'AXISMOTION':
                # ignore small values or else we might get triggered
                # by noise
                if abs(event['value']) > 0.5:
                    self._parent_window.get_settings()[control] = event['axis']
                    # update the button's text widget
                    if self._textwidgets[control]:
                        bui.textwidget(
                            edit=self._textwidgets[control],
                            text=self._parent_window.get_control_value_name(
                                control
                            ),
                        )
                    builtinassets.audio.gun_cocking.play()
                    dialog.die()
        else:
            if event['type'] == 'BUTTONDOWN':
                value = event['button']
                self._parent_window.get_settings()[control] = value
                # update the button's text widget
                if self._textwidgets[control]:
                    bui.textwidget(
                        edit=self._textwidgets[control],
                        text=self._parent_window.get_control_value_name(
                            control
                        ),
                    )
                builtinassets.audio.gun_cocking.play()
                dialog.die()

    def _done(self) -> None:
        bui.containerwidget(edit=self._root_widget, transition='out_scale')
