# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/05_axis.ipynb.

# %% auto 0
__all__ = ['naxis']

# %% ../nbs/05_axis.ipynb 4
from typing import (Iterable)
from atyp import AnyQ, BoolQ, ListQ, FloatQ, ClsBool, ClsStrQ
from synset import synset

# %% ../nbs/05_axis.ipynb 6
class naxis(synset):
    itemattr: ClsStrQ = 'aidx'    
    fullrepr: ClsBool = False

    lbracket: ClsStrQ = '{'
    rbracket: ClsStrQ = '}'

    def makecopy(self, *args, **kwargs):
        '''Call class constructor with the same attributes as the current instance.'''
        return type(self)(self, self.axis, *self.syns, *args, **self.getattrs(**kwargs))

    def __new__(
        cls, name: str, axis: int, *synonyms: Iterable[str],
        icase:  BoolQ   = True,  # Case insensitive
        ispace: BoolQ   = True,  # Space insensitive
        iunder: BoolQ   = True,  # Underscore insensitive
        cutoff: FloatQ  = 0.8,   # Fuzzy cutoff        
        fullrepr: BoolQ = False, # Full representation
        fuzzsyns: BoolQ = True,  # Make all synonyms fuzstrs
        **kwargs
    ):
        # Set dynamic attributes to prevent errors
        itemattr = 'aidx' 
        synsattr = 'synonyms'
        
        # Put all variables in a dictionary for easy access
        aset = {str(axis), } or set()
        synonyms = ({name, *synonyms} if synonyms else {name, }) | aset
        kwargs.update(
            icase=icase, ispace=ispace, iunder=iunder, cutoff=cutoff,
            fullrepr=fullrepr, fuzzsyns=fuzzsyns,
            itemattr=itemattr, synsattr=synsattr
        )
        kwargs[itemattr] = axis
        kwargs[synsattr] = synonyms
        obj = super().__new__(cls, name, *synonyms, **kwargs)
        return obj
    
    def __init__(
        self, name: str, axis: int, *synonyms: Iterable[str],
        icase:  BoolQ   = True,  # Case insensitive
        ispace: BoolQ   = True,  # Space insensitive
        iunder: BoolQ   = True,  # Underscore insensitive
        cutoff: FloatQ  = 0.8,   # Fuzzy cutoff        
        fullrepr: BoolQ = False, # Full representation
        fuzzsyns: BoolQ = True,  # Make all synonyms fuzstrs
        **kwargs
    ):
        # Set dynamic attributes to prevent errors
        itemattr = 'aidx' 
        synsattr = 'synonyms'
        
        # Put all variables in a dictionary for easy access
        aset = {str(axis), } or set()
        synonyms = ({name, *synonyms} if synonyms else {name, }) | aset
        kwargs.update(
            icase=icase, ispace=ispace, iunder=iunder, cutoff=cutoff,
            fullrepr=fullrepr, fuzzsyns=fuzzsyns,
            itemattr=itemattr, synsattr=synsattr
        )
        kwargs[itemattr] = axis
        kwargs[synsattr] = synonyms
        super().__init__(name, *synonyms, **kwargs)

    @property
    def name(self): return self.canon
    @property
    def axis(self): return self.item
    @axis.setter
    def axis(self, value): 
        self.setdattr('itemattr', value)

    def __str__(self):
        return f'{self.canon}{type(self).lbracket}{self.item}{type(self).rbracket}'

    def __repr__(self):
        if not self.fullrepr:
            return str(self)
        return f'{self.clsname}{type(self).lbracket}name="{self.name}", axis={self.item}{type(self).rbracket}'
    
    @classmethod
    def nextaxis(cls, axes: ListQ = None) -> int:
        '''Get the next available axis (integer)'''
        axes, strs, nums = (axes or []), set(), set()

        for axis in axes:

            if isinstance(axis, cls):
                strs.add(axis.name)
                nums.add(axis.axis)

            elif isinstance(axis, str):
                strs.add(axis)

            elif isinstance(axis, int):
                nums.add(axis)

        nums = sorted(list(nums))
        pairs = list(zip(nums, nums[1:]))
        
        nset = set(nums) or set({-1, })
        axis = max(max(nset) + 1, 0)
        
        for a, b in pairs:
            if a + 1 != b:
                axis = a + 1
                break
        return axis

    @classmethod
    def fromstr(cls, axis: str, axes: ListQ = None, **kwargs):
        next = cls.nextaxis(axes=kwargs.get('axes', axes or []))
        if not isinstance(axis, cls) and issubclass(type(axis), str):
            return cls(axis, axis=next, **kwargs)
        return axis  

    @property
    def known(self) -> set: 
        '''Get all known synonyms (includes the axis as a string)'''
        return {self.canon, *self.alts, *(str(self.axis) or set({}))}
    
    def isitem(self, other: AnyQ) -> bool:
        '''Check if `other` is the instance's item'''        
        if isinstance(other, type(self)) or hasattr(other, 'axis'):
            return self.axis == other.axis
        return self.axis == other
    
    def __eq__(self, other):
        if isinstance(other, (type(self))):
            return self.iscanon(other.canon) and self.issyn(other) and self.isitem(other)
        elif isinstance(other, (str, int)):
            return self.iscanon(other) or self.issyn(other) or self.isitem(other)
        return other in self
    
    def __hash__(self):
        return super().__hash__()
