# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_meta.ipynb.

# %% auto 0
__all__ = ['DataTypedEnumMeta', 'FallbackEnumMeta', 'TypedEnumMeta']

# %% ../nbs/01_meta.ipynb 4
from enum import EnumMeta, auto
from typing import Optional, Type, List, Tuple, Any, Union, Dict

# %% ../nbs/01_meta.ipynb 6
class DataTypedEnumMeta(EnumMeta):
    '''Dataclass-like type checking for Enums

    Parameters
    ----------
    dtype : float, optional
        The data-type of the enum.
    '''

    # Dataclass-like type checking
    dtype: Optional[Type] = None

# %% ../nbs/01_meta.ipynb 8
class FallbackEnumMeta(EnumMeta):
    '''Fallback (default) member support for Enums

    Parameters
    ----------
    fbmem : any, optional
        The fallback member to use if provided a missing or invalid enum member

    force : bool, optional
        Whether or not to force fallback by using `cls.members` if fallback member is `None`
    '''
    # Fallback Member to use if provided a missing or invalid enum member
    fbmem: Optional[Any] = None

    # Whether or not to force fallback by using `cls.members` if fallback member is None
    force: Optional[bool] = False

# %% ../nbs/01_meta.ipynb 10
class TypedEnumMeta(DataTypedEnumMeta, FallbackEnumMeta):
    '''Typed enums with fallback members.

    Parameters
    ----------
    dtype : float, optional
        The data-type of the enum.

    fbmem : any, optional
        The fallback member to use if provided a missing or invalid enum member
    '''
    @classmethod
    def __prepare__(metacls, name, bases, **kwargs):
        '''Prepare the class namespace for the enum class

        Parameters
        ----------
        name : str
            Name of the enum class being prepared.
        bases : tuple
            Base classes of the enum class being prepared.
        **kwargs : dict
            Additional keyword arguments.

        Returns
        -------
        dict
            Dictionary for the class namespace.
        '''
        enum_dict = super().__prepare__(name, bases, **kwargs)
        # NOTE: this will through an error since we are using StrEnum
        # enum_dict['fbmem'] = None
        return enum_dict

    def __init__(self, clsname, bases, clsdict, **kwargs):
        '''Initialize the enum class

        Parameters
        ----------
        clsname : str
            Name of the enum class being initialized.
        bases : tuple
            Base classes of the enum class being initialized.
        clsdict : dict
            Dictionary of class attributes and methods.
        **kwargs : dict
            Additional keyword arguments.
        '''
        super().__init__(clsname, bases, clsdict, **kwargs)

    def __new__(
        metacls, cls, bases, clsdict, *, 
        dtype: Optional[Type] = None,
        fbmem: Optional[Union[str, Any]] = None,
        force: Optional[bool] = False,
        items: Optional[List[str]] = None,
    ):
        '''Create a new instance of the enum class

        Parameters
        ----------
        cls : str
            Name of the new class.
        bases : tuple
            Base classes of the new class.
        clsdict : dict
            Dictionary of class attributes and methods.
        dtype : type, optional
            Data type of the enum.
        fbmem : any, optional
            Fallback member of the enum.
        force: bool, default: False
            force failback by leveraging `cls.members[0]` if `fbmem` is not provided
        items : list of str, optional
            Enum members.

        Returns
        -------
        Enum
            The new enum class.
        '''
        if items is not None:
            for item in items:
                clsdict[item.upper()] = auto()

        new_cls = super().__new__(metacls, cls, bases, clsdict)

        # NOTE: this will result in TypeError: cannot extend 
        if fbmem:
            setattr(new_cls, 'fbmem', fbmem)

        if force:
            setattr(new_cls, 'force', force)

        if dtype:
            setattr(new_cls, 'dtype', dtype)
        
        return new_cls

    @property
    def members(cls) -> List[str]:
        '''List of names of the enum members.'''
        # NOTE: could also use cls._member_names_
        return [member.name for member in cls]

    @property
    def values(cls) -> List[Any]:
        '''List of values of the enum members.'''
        return [member.value for member in cls]
    
    @property
    def memdict(cls) -> Dict[str, Any]:
        '''Dict of (name, value) pairs of the enum members.'''
        return dict(zip(cls.members, cls.values))

    @property
    def items(cls) -> Dict[str, Any]:
        '''The items iterator for the dict of (name, value) pairs of the enum members.'''
        return cls.memdict.items()

    @property
    def tuples(cls) -> List[Tuple[str, Any]]:
        '''List of (name, value) pairs of the enum members.'''
        return list(zip(cls.members, cls.values))

    @property
    def clsname(cls) -> str:
        '''Name of the enum class.'''
        return cls.__name__
    
    @property
    def has_fbmem(cls) -> Optional[bool]:
        '''Check if the enum class has a fallback member.'''
        return hasattr(cls, 'fbmem') and getattr(cls, 'fbmem', None) is not None    
