import logging
import sys
import types
from enum import Enum
from typing import List, Set
from ..api.data import PluginStateMetric, BaseMetric
from ..api.selectors import FromPluginSelector

log = logging.getLogger(__name__)

# this is singleton factory for different auto generated classes
class ClassFactory:
    # singleton pattern
    __instance = None
    def __new__(cls):
        if ClassFactory.__instance is None:
            ClassFactory.__instance = object.__new__(cls)
            ClassFactory.__instance.__generated_classes = {}
        return ClassFactory.__instance
    
    @property
    def generated_classes(self) -> Set[str]:
        return frozenset(self.__generated_classes.keys())
    
    def addClass(self, key: str, class_object):
        module = class_object.__module__
        generated_module = sys.modules.get(module, None)
        if not generated_module:
            generated_module = types.ModuleType(module)
            sys.modules[module] = generated_module
        setattr(generated_module, class_object.__name__, class_object)
        self.__generated_classes[key] = class_object
        log.info("Class %s was generated: module = %s, class name = %s", class_object, class_object.__module__, class_object.__qualname__) 

def pep8_classname(name: str):
    from io import StringIO
    length = len(name)
    output = StringIO()
    toUpper = True
    for index in range(length):
        character = name[index]
        if character.isalnum():
            if toUpper:
                toUpper = False
                character = character.upper()
            output.write(character)
        else:  
            toUpper = True
    return output.getvalue()
    
def get_class_params(package: str, name: str, name_suffix: str):
    class_name = pep8_classname(name) + name_suffix
    module = 'generated.' + pep8_classname(package)
    class_key = module + '.' + class_name
    return class_name, module, class_key
    
def generate_state_enum(package: str, name: str, states: List[str]):
    class_name, module, class_key = get_class_params(package, name, 'Enum')
    factory = ClassFactory()
    if class_key in factory.generated_classes:
        return
    state_names = []
    count = 0.0
    for state in states:
        state_names.append((state, count))
        count += 1.0
    generated = Enum (value=class_name, names=state_names, module=module)
    factory.addClass (class_key, generated)
    log.info ("Enum values for %s: %s", generated.__module__, dir(generated))
    return generated

_default_selector = FromPluginSelector()

def generate_state_metric(package: str, name: str):
    class_name, module, class_key = get_class_params(package, name, 'Metric')
    factory = ClassFactory()
    if class_key in factory.generated_classes:
        return
 
    def __init__(self, value, dimensions=None, entity_selector=None):
        PluginStateMetric.__init__(self, name, value, dimensions, entity_selector or _default_selector)
        
    generated = type(class_name, (PluginStateMetric,BaseMetric,), dict(__init__ = __init__)) 
    generated.__module__ = module
    factory.addClass (class_key, generated)
    return generated
