# from typing import Callable
from .functions import find_class
from typing import Tuple, Callable  # Any, Optional, Union,


def func_class_name_is_in(cls_name: str, *args: str) -> Callable[[Callable], bool]:
    """
    Crée un prédicat qui renvoie True si le nom de la classe de la fonction/méthode existe et fait partie des valeurs données.

    :param cls_name: nom de classe
    :type cls_name: str

    :param args: noms supplémentaires
    :type args: str (multiple)

    :return: filtre
    :rtype: Callable[[Callable], bool]
    """
    values = (cls_name, *args)

    def func_class_name_is_in(func: Callable) -> bool:
        cls = find_class(func)
        return bool(cls) and cls.__name__ in values
    return func_class_name_is_in


def func_class_name_is_not_in(cls_name: str, *args: str) -> Callable[[Callable], bool]:
    """
    Crée un prédicat qui renvoie True si le nom de la classe de la fonction/méthode existe et ne fait pas partie des valeurs données.

    :param cls_name: nom de classe
    :type cls_name: str

    :param args: noms supplémentaires
    :type args: str (multiple)

    :return: filtre
    :rtype: Callable[[Callable], bool]
    """
    values = (cls_name, *args)

    def func_class_name_is_not_in(func: Callable) -> bool:
        cls = find_class(func)
        return bool(cls) and cls.__name__ not in values
    return func_class_name_is_not_in


def func_name_is_in(name: str, *args: str) -> Callable[[Callable], bool]:
    """
    Crée un prédicat qui renvoie True si le nom de la fonction/méthode fait partie des valeurs données.

    :param name: nom de fonction/méthode
    :type name: str

    :param args: noms supplémentaires
    :type args: str (multiple)

    :return: filtre
    :rtype: Callable[[Callable], bool]
    """
    values = (name, *args)

    def func_name_is_in(func: Callable) -> bool:
        return func.__name__ in values
    return func_name_is_in


def func_name_is_not_in(name: str, *args: str) -> Callable[[Callable], bool]:
    """
    Crée un prédicat qui renvoie True si le nom de la fonction/méthode ne fait pas partie des valeurs données.

    :param name: nom de fonction/méthode
    :type name: str

    :param args: noms supplémentaires
    :type args: str (multiple)

    :return: filtre
    :rtype: Callable[[Callable], bool]
    """
    values = (name, *args)

    def func_name_is_not_in(func: Callable) -> bool:
        return func.__name__ not in values
    return func_name_is_not_in


def func_is_public(func: Callable) -> bool:
    """
    Prédicat qui renvoie True si la fonction/méthode est "publique".
    (les notions de public, protected, private ne sont que des conventions de nommage en Python)

    :return: filtre
    :rtype: Callable[[Callable], bool]
    """
    return not func.__name__.startswith('_')
