"""A simple enumeration class.
"""


__copyright__ = """Copyright (C) 2020 Corso Systems"""
__license__ = 'Apache 2.0'
__maintainer__ = 'Andrew Geiger'
__email__ = 'andrew.geiger@corsosystems.com'

__all__ = ['Enum']


class MetaEnumValue(type):
    
    def __init__(cls, clsname, bases, attributes):
        for base in bases:
            if base.__name__ != 'EnumValue':
                setattr(cls, '_type', base)
                break
        
        def __setattr__readonly__(self, key, _):
            raise AttributeError("<%r> is readonly" % self)
        setattr(cls, '__setattr__', __setattr__readonly__)

        
class EnumValue(object):
    _parent = None
    _type = None
        
    # def __str__(self):
    #     return '%s.%s' % (self._parent.__name__, self.__class__.__name__)

    def __repr__(self):
        return '<%s.%s %s>' % (self._parent.__name__, 
                               self.__class__.__name__, 
                               self._type.__str__(self))
    

    
class MetaEnum(type):
    
    _initFields = ('_fields', '_values')
    _class_initialized = False
        
    def __init__(cls, clsname, bases, attributes):
        
        super(MetaEnum, cls).__setattr__('_class_initialized', False) # bypass interlock
        
        fvs = [(key,value) for key, value in attributes.items() if not key.startswith('_')]
        
        if fvs:
            fields,values = zip(*sorted(fvs, key=lambda (key,value): value))
        
            setattr(cls, '_fields', fields)
            setattr(cls, '_values', values)
            
            for key,value in fvs:
                EnumAttribute = MetaEnumValue(key, (EnumValue,type(value)), {'_parent': cls})
                setattr(cls, key, EnumAttribute(value))

            
        else:
            setattr(cls, '_fields', tuple())
            setattr(cls, '_values', tuple())
        
        cls._class_initialized = True
                
            
    def __setattr__(cls, key, value):
        if cls._class_initialized:
            raise AttributeError("<%s> attributes are readonly" % cls.__name__)
        else:
            super(MetaEnum, cls).__setattr__(key, value)                
                
    def __contains__(cls, enum_key):
        return enum_key in cls._fields
    
    def keys(cls):
        return cls._fields

    def values(cls):
        return cls._values

    def __iter__(cls):
        return iter(getattr(cls, field) for field in cls._fields)
    
    def __getitem__(cls, attribute):
        return getattr(cls, attribute)
    
    def __str__(cls):
        return cls.__name__
    
    def __repr__(cls):
        return "<%s {%s}>" % (cls.__name__, 
                              ', '.join("%s: %s" % (repr(field), repr(value)) 
                                        for field, value in zip(cls._fields, cls._values)))

    
class Enum(object):
    __metaclass__ = MetaEnum
    __slots__ = tuple()
        
    _fields = tuple()
    _values = tuple()
    
    def __new__(cls, value=None):
        if value is not None and value in cls._values:
            return getattr(cls, cls._fields[[i for i,v in enumerate(cls._values) if v == value][0]])
        raise KeyError("%s is an enumeration and %r is not a valid value." % (cls.__name__, value)) 
    
    def __init__(cls):
        raise NotImplementedError("%s is an enumeration and does not support instantiation." % cls.__name__)
        
    def __setattr__(cls, key, value):
        raise AttributeError("<%s> attributes are readonly" % cls.__name__)
