import gi

gi.require_version("Gtk", "4.0")
from gi.repository import Gio, GObject, Gtk

from typing import Any, List, Tuple

from iotas.category import Category, CategorySpecialPurpose


class CategoryListModel(Gtk.SortListModel):
    ALL_INDEX = 0
    UNCATEGORISED_INDEX = 1

    def __init__(self):
        self.__all_category = None
        self.__uncategorised_category = None
        self.__store = Gio.ListStore(item_type=Category)
        self.__sorter = Gtk.CustomSorter()
        super().__init__(model=self.__store, sorter=self.__sorter)

    def populate(self, categories: List[Category]) -> None:
        """Populate store with categories.

        :param List[Category] categories: Categories to populate
        """
        self.__store.remove_all()
        # Populate reversed so children are created before parents
        for category in reversed(categories):
            self.__store.append(category)
            if category.special_purpose == CategorySpecialPurpose.ALL:
                self.all_category = category
            elif category.special_purpose == CategorySpecialPurpose.UNCATEGORISED:
                self.uncategorised_category = category
        self.invalidate_sort()

    def add(self, category: Category) -> None:
        """Add category to store.

        :param Category category: Category to remove
        """
        index_before = 1
        for i in range(2, self.__store.get_n_items()):
            list_cat = self.__store.get_item(i)
            if list_cat.special_purpose is not None:
                continue
            if list_cat.name.lower() > category.name.lower():
                break
            index_before = i
        self.__store.insert(index_before + 1, category)

    def remove(self, category: Category) -> None:
        """Remove category from store.

        :param Category category: Category to remove
        """
        for i in range(0, self.__store.get_n_items()):
            list_cat = self.__store.get_item(i)
            if list_cat.special_purpose is not None:
                continue
            if list_cat.name == category.name:
                self.__store.remove(i)
                break

    def get_special(self, purpose: CategorySpecialPurpose) -> Tuple[Category, int]:
        """Fetch a special purpose category.

        :param CategorySpecialPurpose purpose: The category a note has been removed from
        :return: The category and index in store
        :rtype: Tuple[Category, int]:
        """
        category = None
        index = -1
        if purpose == CategorySpecialPurpose.ALL:
            category = self.all_category
            index = self.ALL_INDEX
        elif purpose == CategorySpecialPurpose.UNCATEGORISED:
            category = self.uncategorised_category
            index = self.UNCATEGORISED_INDEX
        return (category, index)

    def get_non_special(self, name: str) -> Tuple[Category, int]:
        """Fetch a non-special purpose category.

        :param str name: The category name
        :return: The category and index in store
        :rtype: Tuple[Category, int]:
        """
        category = None
        index = 0
        for i in range(0, self.__store.get_n_items()):
            list_cat = self.__store.get_item(i)
            if list_cat.name == name:
                category = list_cat
                index = i
                break
        return (category, index)

    def invalidate_sort(self) -> None:
        """Invalidate the sorter."""
        self.__sorter.set_sort_func(CategoryListModel.sort_func)

    @GObject.Property(type=Category, default=None)
    def all_category(self) -> Category:
        return self.__all_category

    @all_category.setter
    def all_category(self, new_val: Category) -> None:
        self.__all_category = new_val

    @GObject.Property(type=Category, default=None)
    def uncategorised_category(self) -> Category:
        return self.__uncategorised_category

    @uncategorised_category.setter
    def uncategorised_category(self, new_val: Category) -> None:
        self.__uncategorised_category = new_val

    @staticmethod
    def sort_func(category1: Category, category2: Category, _data: Any) -> int:
        """Sort categories.

        :param Category category1: First category
        :param Category category2: Second category
        :param _data: Unused
        :return: -1 if category1 earlier, 1 if later, 0 unused
        :rtype: int
        """
        if category1.special_purpose == CategorySpecialPurpose.ALL:
            return -1
        elif category2.special_purpose == CategorySpecialPurpose.ALL:
            return 1
        elif (
            category1.special_purpose == CategorySpecialPurpose.UNCATEGORISED
            and category2.special_purpose is None
        ):
            return -1
        elif (
            category2.special_purpose == CategorySpecialPurpose.UNCATEGORISED
            and category1.special_purpose is None
        ):
            return 1
        else:
            return -1 if category1.name.lower() < category2.name.lower() else 1
