from gi.repository import Gdk, GLib, GObject, Gtk

from typing import Callable

from iotas.category import Category, CategorySpecialPurpose
from iotas.category_manager import CategoryManager
import iotas.config_manager


@Gtk.Template(resource_path="/org/gnome/World/Iotas/ui/sidebar_row.ui")
class SidebarRow(Gtk.TreeExpander):
    __gtype_name__ = "SidebarRow"

    FOCUS_CSS_CLASS_NAME = "focused"

    _icon = Gtk.Template.Child()
    _name_label = Gtk.Template.Child()
    _count_with_children_label = Gtk.Template.Child()
    _count_node_only_label = Gtk.Template.Child()

    def __init__(
        self, activated_func: Callable[[Category], None], category_manager: CategoryManager
    ) -> None:
        super().__init__()
        self.__category = None
        self.__bindings = []
        self.__activated_func = activated_func
        category_manager.bind_property(
            "have-sub-category", self, "indent-for-icon", GObject.BindingFlags.SYNC_CREATE
        )

        # Add some (hopefully temporary) handling of the keyboard focus ring
        def manage_manual_css_class(row, _flags: Gtk.StateFlags):
            parent = row.get_property("parent")
            if parent is None:
                return
            if row.get_state_flags() & Gtk.StateFlags.FOCUS_VISIBLE:
                parent.add_css_class(self.FOCUS_CSS_CLASS_NAME)
            else:
                parent.remove_css_class(self.FOCUS_CSS_CLASS_NAME)

        self.connect("state-flags-changed", manage_manual_css_class)

    def bind(self, category: Category) -> None:
        """Bind category to row.

        :param Category category: New category
        """
        self.__category = category
        self.__add_binding(category, "note-count", self._count_node_only_label, "label")
        if self.__category.get_parent() is None:
            # Build for root category
            self.__add_binding(category, "display-name", self._name_label, "label")
            self.__add_binding(
                category, "note-count-with-children", self._count_with_children_label, "label"
            )
            tree_list_row = self.get_list_row()
            self.__add_binding(
                tree_list_row,
                "expanded",
                self._count_with_children_label,
                "visible",
                GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN,
            )
            self.__add_binding(tree_list_row, "expanded", self._count_node_only_label, "visible")

            if category.special_purpose == CategorySpecialPurpose.ALL:
                self._icon.set_from_icon_name("edit-select-all-symbolic")
            elif category.special_purpose == CategorySpecialPurpose.UNCATEGORISED:
                self._icon.set_from_icon_name("grid-symbolic")
        else:
            # Build for sub category
            self.__add_binding(category, "sub-category-name", self._name_label, "label")
            self._count_node_only_label.set_visible(True)
            self._count_with_children_label.set_visible(False)

    def unbind(self) -> None:
        """Unbind category from row."""
        for binding in self.__bindings:
            binding.unbind()
        self.__bindings.clear()
        self.__category = None

    @Gtk.Template.Callback()
    def _activate(self, click: Gtk.GestureClick, _n_press: int, _x: float, _y: float) -> None:
        state = click.get_current_event_state()
        if state == Gdk.ModifierType.ALT_MASK:
            list_row = self.get_list_row()
            if list_row.is_expandable():
                list_row.set_expanded(not list_row.get_expanded())
        else:
            close_sidebar = state != Gdk.ModifierType.CONTROL_MASK
            if iotas.config_manager.get_pin_sidebar():
                self.__activated_func(self.__category, close_sidebar)
            else:
                # Without the idle callback the listview selection doesn't get set before the
                # sidebar closes (when auto-hiding is enabled)
                GLib.idle_add(self.__activated_func, self.__category, close_sidebar)

    def __add_binding(
        self,
        from_object: GObject.Object,
        from_property: str,
        to_object: GObject.Object,
        to_property: str,
        binding_flags: GObject.BindingFlags = GObject.BindingFlags.SYNC_CREATE,
    ) -> None:
        binding = from_object.bind_property(from_property, to_object, to_property, binding_flags)
        self.__bindings.append(binding)
