from .simple_methods import combination_total, all_combinations_unique_elements
from .utils import list_to_bin_dict


class CombinationGenerator:
    """
    CombinationGenerator is designed to generates all possible combinations of elements in a list with a given number of selections.
    The combination is generated by a given iterator number and it is the n-th combination of the all possible combinations.
    As default, the n-th combination is generated by the lexicographic order.
    But, if the priority of the elements is given, the combination is generated by the given priority.

    The elements can be duplicated in the list.

    .. _combinationgenerator:

    """

    def __init__(self):
        """
        Initialize the CombinationGenerator.
        """
        self.element_list_initialized = False
        self.number_of_selection_initialized = False
        self.must_have_elements = False

    def set_parameters(self, in_number_of_selection: int, element_list: list):
        """
        Initialize the CombinationGenerator with the given number of selections and the element list.

        Args:
            in_number_of_selection (int): The number of selections.
            element_list (list): The list of elements.
        """
        self.in_number_of_selection = in_number_of_selection
        self.element_list = element_list

        self.element_list_initialized = True
        self.number_of_selection_initialized = True
        # self.max_possible = combination_total(len(element_list), in_number_of_selection)

    def all_cases(self):
        """
        Return all possible combinations of the list.

        Returns:
            list: All possible combinations of the list.
        """
        return self._recursive_combination(
            list_to_bin_dict(self.element_list), self.in_number_of_selection
        )

    def _recursive_combination(self, in_dict: dict, left_length: int, initial=True):
        result = []
        if left_length == 1:
            for key in in_dict.keys():
                if in_dict[key] > 0:
                    result.append([key])
            return result
        if left_length > 0:
            for key in in_dict.keys():
                if in_dict[key] > 0:
                    temp_dict = in_dict.copy()
                    temp_dict[key] -= 1
                    temp_result = self._recursive_combination(
                        temp_dict, left_length - 1, False
                    )
                    for temp in temp_result:
                        result.append([key] + temp)
            if initial:
                sorted_result = list(map(lambda x: sorted(x), result))
                filtered_result = []
                for i in sorted_result:
                    if i not in filtered_result:
                        filtered_result.append(i)
                return filtered_result
            return result
        return result

    def n_th_case(self, in_iterator: int) -> list:
        return self.all_cases()[in_iterator]

    def n_to_m_th_cases(self, in_n_iterator: int, in_m_iterator: int) -> list:
        result = self.all_cases()
        if in_m_iterator > len(result) - 1:
            print(
                "The m-th iterator is larger than the number of all possible combinations."
            )
            in_m_iterator = len(result) - 1
        return self.all_cases()[in_n_iterator : in_m_iterator + 1]
