import inspect
from .settings import ValidationError, Setting
from saleae.data import SaleaeTime


def format_setting_label(member_name: str):
    # Replace '_' and split
    words = member_name.replace('_', ' ').split(' ')

    # Remove whitespace
    words = (word.strip() for word in words)

    # Capitalize first letter of each word
    words = (word[0].upper() + word[1:] for word in words)

    # Join with spaces
    return ' '.join(words)


# HLA Metaclass. The only purpose of this class is to customize the calls to __new__ and __init__. Internally we
# pass a settings object to __new__, but want to strip that out for the call to __init__.
class MetaHighLevelAnalyzer(type):
    def __call__(cls, settings, *args, **kwargs):
        obj = cls.__new__(cls, settings, *args, **kwargs)
        obj.__init__(*args, **kwargs)
        return obj


class HighLevelAnalyzer(metaclass=MetaHighLevelAnalyzer):
    @classmethod
    def _get_settings(cls):
        '''Return a list of the settings for this class'''

        if hasattr(cls, '_settings'):
            return cls._settings

        settings = []

        for name, value in inspect.getmembers(cls):
            if isinstance(value, Setting):
                # The name of the setting is the name of the class member it is assigned to.
                # If a label hasn't been set, the name of the setting is used. We have to do this here, instead
                # in the Setting object, because the Setting object does not know its own name when instantiated.
                if value.label is None:
                    value.label = format_setting_label(name)
                value.name = name
                settings.append(value)

        cls._settings = settings

        return cls._settings

    @classmethod
    def _get_settings_serialized(cls):
        '''Return a serialized version of _get_settings that flattens the Setting objects to plain old python types.'''

        settings = {}

        for setting in cls._get_settings():
            settings[setting.name] = setting._serialize()

        return settings

    def __new__(cls, settings, *args, **kwargs):
        obj = super(HighLevelAnalyzer, cls).__new__(cls, *args, **kwargs)

        for setting in cls._get_settings():
            name = setting.name
            if name not in settings:
                raise ValidationError('Missing setting: ' + name)
            setting.validate(settings[name])
            setattr(obj, name, settings[name])

        return obj


class AnalyzerFrame:
    def __init__(self, type: str, start_time: SaleaeTime, end_time: SaleaeTime, data: dict = None):
        self.type = type
        self.start_time = start_time
        self.end_time = end_time
        self.data = data
