from gi.repository import Adw, Gio, GLib, GObject, Gtk

import logging
import time


@Gtk.Template(resource_path="/org/gnome/World/Iotas/ui/table_dialog.ui")
class TableDialog(Adw.Dialog):
    """Table creation dialog."""

    __gtype_name__ = "TableDialog"

    __gsignals__ = {
        "create": (GObject.SignalFlags.RUN_FIRST, None, (int, int)),
    }

    GRID_WIDTH = 6
    GRID_HEIGHT = 5
    DEFAULT_HEIGHT = 3
    DEFAULT_WIDTH = 3

    _size_grid = Gtk.Template.Child()
    _size_label = Gtk.Template.Child()
    _button = Gtk.Template.Child()

    def __init__(self) -> None:
        super().__init__()
        self.__width = self.DEFAULT_WIDTH
        self.__height = self.DEFAULT_HEIGHT
        self.__hover_timeout_id = None
        self.__hover_until = 0
        self.__build()
        self.connect("closed", lambda _o: self.__cleanup())

    def show(self, parent: Gtk.Window) -> None:
        """Show dialog.

        :param Gtk.Window window: Parent window
        """
        self.width = self.DEFAULT_WIDTH
        self.height = self.DEFAULT_HEIGHT
        for action in self.__action_group.list_actions():
            self.__action_group.lookup_action(action).set_enabled(True)
        self.present(parent)

    @GObject.Property(type=int)
    def width(self) -> int:
        return self.__width

    @width.setter
    def set_width(self, value: int) -> None:
        self.__width = value
        self.__update_grid()

    @GObject.Property(type=int)
    def height(self) -> int:
        return self.__height

    @height.setter
    def set_height(self, value: int) -> None:
        self.__height = value
        self.__update_grid()

    @Gtk.Template.Callback()
    def _on_button_clicked(self, _obj: Gtk.Button) -> None:
        self.__create()

    def __build(self) -> None:
        for row in range(self.GRID_HEIGHT):
            for column in range(self.GRID_WIDTH):
                button = Gtk.Button.new()
                self._size_grid.attach(button, column, row, 1, 1)

                def clicked(_o, width, height) -> None:
                    logging.info(f"Chose {width}x{height}")
                    self.width = width
                    self.height = height

                button.connect("clicked", clicked, column + 1, row + 1)

                controller = Gtk.EventControllerMotion.new()
                controller.connect("enter", self.__on_motion_enter, row, column)
                button.add_controller(controller)

        controller = Gtk.EventControllerMotion.new()
        controller.connect("leave", lambda _o: self.__on_motion_leave())
        self._size_grid.add_controller(controller)

        action_group = Gio.SimpleActionGroup.new()
        app = Gio.Application.get_default()

        action = Gio.SimpleAction.new("submit")
        action.connect("activate", lambda _a, _p: self.__create())
        action_group.add_action(action)
        app.set_accels_for_action("table-dialog.submit", ["<Alt>Return"])

        app.get_active_window().insert_action_group("table-dialog", action_group)
        self.__action_group = action_group

    def __update_grid(self) -> None:
        label = f"{self.width} x {self.height}"
        self._size_label.set_text(label)
        for row in range(self.GRID_HEIGHT):
            for column in range(self.GRID_WIDTH):
                button = self._size_grid.get_child_at(column, row)
                if self.width >= column + 1 and self.height >= row + 1:
                    button.add_css_class("on")
                else:
                    button.remove_css_class("on")

    def __on_motion_enter(
        self, motion: Gtk.EventControllerMotion, x: float, y: float, row: int, column: int
    ) -> None:
        # Hacky avoidance of motion on possibly mobile device. Without this (in Sept. 2024) a hover
        # pseudoclass is left on the buttons.
        if self.get_width() <= 360:
            return

        # If no motion hide hover after a second
        self.__hover_until = time.time() + 1
        if self.__hover_timeout_id is None:
            self.__hover_timeout_id = GLib.timeout_add(
                1000,
                self.__hover_timeout,
            )

        self.__update_hover(row, column)

    def __on_motion_leave(self) -> None:
        if self.__hover_timeout_id is not None:
            GLib.source_remove(self.__hover_timeout_id)
            self.__hover_timeout_id = None
        self.__update_hover(-1, -1)

    def __update_hover(self, hover_row: int, hover_column: int) -> None:
        for row in range(self.GRID_HEIGHT):
            for column in range(self.GRID_WIDTH):
                button = self._size_grid.get_child_at(column, row)
                if hover_column >= column and hover_row >= row:
                    button.add_css_class("hovering")
                else:
                    button.remove_css_class("hovering")

    def __hover_timeout(self) -> None:
        extra_hover = self.__hover_until - time.time()
        if extra_hover > 0.0:
            self.__hover_timeout_id = GLib.timeout_add(
                extra_hover * 1000.0,
                self.__hover_timeout,
            )
            return

        self.__update_hover(-1, -1)
        self.__hover_timeout_id = None

    def __create(self) -> None:
        self.emit("create", self.width, self.height)
        self.close()

    def __cleanup(self) -> None:
        for action in self.__action_group.list_actions():
            self.__action_group.lookup_action(action).set_enabled(False)
