# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/02_enum.ipynb.

# %% auto 0
__all__ = ['Litum', 'Strum', 'Intum', 'TypedFallbackMixin', 'BaseTypedEnum', 'OperatorEnum', 'LiteralEnum', 'LitStrEnum',
           'LitIntEnum']

# %% ../nbs/02_enum.ipynb 4
from enum import Enum, IntEnum, StrEnum, auto
from typing import Optional, Callable, List, Tuple, Any, Dict, Union, Literal
from dataclasses import dataclass

# %% ../nbs/02_enum.ipynb 5
from .decs import set_attr, set_default, set_fallback, set_fbmem, set_method, set_dtype, set_repr
from .meta import DataTypedEnumMeta, FallbackEnumMeta, TypedEnumMeta

# %% ../nbs/02_enum.ipynb 7
class TypedFallbackMixin(metaclass=TypedEnumMeta):
    '''Mixin providing the fallback functionality for the TypedEnum class.'''

    @staticmethod
    def val_is_member_name(mem, val) -> bool:
        '''Check if the value matches the member name.

        Parameters
        ----------
        mem : EnumMember
            The enum member to check against.
        val : any
            The value to check.

        Returns
        -------
        bool
            True if the value matches the member name, False otherwise.
        '''
        return str(mem.name).lower() == str(val).lower()

    @staticmethod
    def val_is_member_value(mem, val) -> bool:
        '''Check if the value matches the member value.

        Parameters
        ----------
        mem : EnumMember
            The enum member to check against.
        val : any
            The value to check.

        Returns
        -------
        bool
            True if the value matches the member value, False otherwise.
        '''
        return str(mem.value).lower() == str(val).lower()

    @classmethod
    def val_matches_member(cls, mem, val) -> bool:
        '''Check if the value matches the member name or value.

        Parameters
        ----------
        mem : EnumMember
            The enum member to check against.
        val : any
            The value to check.

        Returns
        -------
        bool
            True if the value matches the member name or value, False otherwise.
        '''
        is_key = cls.val_is_member_name(mem, val)
        is_val = cls.val_is_member_value(mem, val)
        return is_key or is_val

    @classmethod
    def match_val_to_member(cls, val: Any) -> Optional[Any]:
        '''Find the enum member that matches the value.

        Parameters
        ----------
        val : any
            The value to match.

        Returns
        -------
        EnumMember or None
            The matching enum member or None if not found.
        '''
        if val is None: 
            return None            
        for mem in cls:
            if cls.val_matches_member(mem, val):
                return mem
        return None

    @classmethod
    def check_dtype(cls, mem) -> bool:
        '''Check if the enum member matches the enum's data type.

        Parameters
        ----------
        mem : EnumMember
            The enum member to check.

        Returns
        -------
        bool
            True if the member matches the data type, False otherwise.
        '''
        if not hasattr(cls, 'dtype'):
            return False

        dtype = cls.dtype
        if not isinstance(dtype, type):
            dtype = type(dtype)

        return isinstance(mem, dtype)

    @classmethod
    def check_fallback(cls, force: bool = False) -> Optional[Any]:
        '''Check for a fallback member or value.

        Parameters
        ----------
        force : bool, default: False
            If True, force the fallback to the first member if no fallback is found. Default is False.

        Returns
        -------
        EnumMember : any, default: None
            The fallback member or value, or None if not found.
        '''
        
        fback = None

        if hasattr(cls, 'fbmem'):
            fbmem = getattr(cls, 'fbmem', None)            
            if isinstance(fbmem, cls):
                fback = fbmem

            elif cls.check_dtype(fbmem):
                fback = fbmem
                try:
                    fback = cls.match_val_to_member(fback)
                    if fback is not None:
                        return fback
                except:
                    fback = None

            elif isinstance(fbmem, str):
                fback = getattr(cls, fbmem, None)

        if fback is None and force:
            forced_member = cls.members[0]
            return cls._missing_(forced_member)

        return fback

    @classmethod
    def _missing_(cls, val: Any) -> Optional[Any]:
        '''Handle missing enum members.

        Parameters
        ----------
        val : any
            The val to match.

        Returns
        -------
        EnumMember : any, default: None
            The matching or fallback member or val or None if not found.
        '''      
        found = cls.match_val_to_member(val)
        if found is not None:
            return found

        fback = cls.check_fallback(force=getattr(cls, 'force', True))
        return fback

# %% ../nbs/02_enum.ipynb 11
class BaseTypedEnum(TypedFallbackMixin, Enum):
    '''Base class for typed enums with fallback functionality.

    Examples
    --------
    >>> class MyEnum(BaseTypedEnum, dtype=int, fbmem='THREE'):
    ...    ONE = 1
    ...    TWO = 2
    ...    THREE = 3

    >>> print(MyEnum(1))
    MyEnum.ONE

    >>> print(MyEnum('missing'))
    MyEnum.THREE
    '''   
    pass

# %% ../nbs/02_enum.ipynb 16
class OperatorEnum(BaseTypedEnum):#, Enum, metaclass=TypedEnumMeta):
    '''Enum for operator functions.

    Examples
    --------
    >>> import operator
    >>> class MyOpEnum(OperatorEnum):
    ...    EQ = operator.eq
    ...    NE = operator.ne

    >>> eq = MyOpEnum('EQ')
    >>> print(eq(1, 1))
    True

    >>> ne = MyOpEnum('NE')
    >>> print(ne(1, 2))
    True
    '''    
    def __call__(self, *args, **kwargs) -> bool:
        '''Call the operator function.

        Parameters
        ----------
        *args
            Arguments to pass to the operator function.
        **kwargs
            Keyword arguments to pass to the operator function.

        Returns
        -------
        bool
            The result of the operator function.
        '''
        func = self.value
        return func(*args, **kwargs)

# %% ../nbs/02_enum.ipynb 21
class LiteralEnum(BaseTypedEnum, StrEnum, metaclass=TypedEnumMeta):
    '''Enum for stringly-typed enums like a keyword argument with a few valid str values.

    Examples
    --------
    >>> class MyLiteralEnum(LiteralEnum):
    ...    RED = 'red'
    ...    BLUE = 'blue'
    ...    GREEN = 'green'

    >>> color = MyLiteralEnum('RED')
    >>> print(color)
    MyLiteralEnum.RED

    >>> try:
    ...     invalid_color = MyLiteralEnum('ORANGE')
    ... except ValueError as e:
    ...     print(e)
    'ORANGE' is not a valid MyLiteralEnum
    '''
    pass

Litum = LiteralEnum

# %% ../nbs/02_enum.ipynb 28
class LitStrEnum(BaseTypedEnum, StrEnum, metaclass=TypedEnumMeta):    
    pass

Strum = LitStrEnum

# %% ../nbs/02_enum.ipynb 30
class LitIntEnum(BaseTypedEnum, IntEnum, metaclass=TypedEnumMeta):    
    pass

Intum = LitIntEnum
