import yaml as yaml
from astropy import units
import re

class DefaultParser:
    """Not invented here syndrome"""

    __check = {}
    __convert = {}
    __list_of_leaf_types = []


    def __init__(self, default_dict):
        
        self.__register_leaf('list')
        self.__register_leaf('int')
        self.__register_leaf('float')
        self.__register_leaf('quantity')
        self.__register_leaf('string')
        self.__register_leaf('container-declaration')
        self.__mandatory = False


        self.__allowed_value = None
        self.__allowed_type = None
        self.__config_value = None
        self.__path = None

        self.__default_dict = default_dict

        if not 'property_type' in default_dict:
            self.__property_type = 'arbitrary'
        else:
            self.__property_type = default_dict['property_type']
            if not self.__property_type in self.__check:
                raise ValueError 

        if 'allowed_value' in default_dict:
            self.__allowed_value = self.__convert_av_in_pt(default_dict['allowed_value'], self.__property_type)

        if 'allowed_type' in default_dict:
            self.__allowed_type = default_dict['allowed_type']
            self.__lower, self.__upper = self.__parse_allowed_type(self.__allowed_type)

        if 'default' in default_dict:
            self.set_default(default_dict['default'])

        if 'mandatory' in default_dict:
            self.__mandatory =  default_dict['mandatory']

    

        self.is_leaf = self.__is_leaf(self.__property_type)

    def get_default(self):
        return self.__default_value

    def set_default(self, value):
        if value != None:
            if self.is_valid(value):
                self.__default_value = value
            else:
                raise ValueError('Default value violates property constraint.')
        else:
            self.__default_value = None

    def is_mandatory(self):
        return self.__mandatory

    def has_default(self):
        try:
            if self.__default_value:
                return True
            else:
                return False
        except NameError:
            pass

    def set_path_in_dic(self, path):
        self.__path = path
        
    def get_path_in_dict(self):
        return self.__path

    def set_config_value(self, value):
        self.__config_value = value

    def get_value(self):
        
        if self.__config_value is not None and  self.is_valid(self.__config_value):
            return self.__config_value
        else:
            if self.has_default():
               return self.__default_value
            else:
               raise ValueError('No default value given.')

    def is_container(self):
        return self.__is_container(None)

    def get_container_dic(self):
        if self.__is_container(None):
            return self.__container_dic

    def update_container_dic(self, container_dic, current_entry_name):
        if reduce(lambda a,b: a or b, [container_dic
.has_key(i) fo
r i in ['and','or']], True):
            if 'or' in container_dic:
                if current_entry_name in container_dic['or']:
                    container_dic['or'] = []
                    return container_dic
            if 'and' in container_dic:
                if current_entry_name in container_dic['and']:
                    current_entry_name['and'].remove(current_entry_name)
                    return container_dic






    def is_valid(self, value):
        if not self.__check[self.__property_type](self,value):
            return False
        if self.__allowed_value:
            if not self.__is_allowed_value(self,value, self.__allowed_value):
                return False
        if self.__allowed_type:
            if not self.__check_value(self,value, self.__lower, self.__upper):
                return False
        return True

    def __register_leaf(self,type_name):
        print(type_name)
        if not type_name in self.__list_of_leaf_types:
            self.__list_of_leaf_types.append(type_name)

    def __is_leaf(self, type_name):
        return type_name in self.__list_of_leaf_types

    def __is_container(self, value):
        if self.__property_type == 'container-property':
            try:
                self.__container_dic = self.__default_dict['type']['containers']
                return True
            except:
                return False
        else:
            return False
    __check['container-property'] = __is_container

    def __is_container_declaration(self, value):
        pass


    def __is_type_arbitrary(self, value):
        self.is_leaf = False
        return True

    __check['arbitrary'] = __is_type_arbitrary


    def __is_type_list(self, value):
        self.__register_leaf('list')
        try:
            return isinstance(value, list)
        except ValueError:
            return False

    __check['list'] = __is_type_list


    def __is_type_int(self, value):
        self.__register_leaf('int')
        try:
            int(value)
            if float.is_integer(float(value)):
                return True
            else:
                return False
        except ValueError:
            return False

    __check['int'] = __is_type_int


    def __is_type_float(self, value):
        self.__register_leaf('float')
        try:
            float(value)
            return True
        except ValueError:
            return False

    __check['float'] = __is_type_float


    def __is_type_quantity(self, value):
        self.__register_leaf('quantity')
        try:
            quantity_value, quantity_unit = value.strip().split()
            float(quantity_value)
            units.Unit(quantity_unit)
            return True
        except ValueError:
            return False

    __check['quantity'] = __is_type_quantity

    def __is_type_string(self, value):
        self.__register_leaf('string')
        try:
            str(value)
            return True
        except ValueError:
            return False

    __check['string'] = __is_type_string


    def __to_quantity(self, value):
        quantity_value, quantity_unit = value.strip().split()
        float(quantity_value)
        units.Unit(quantity_unit)
        return (quantity_value, quantity_unit)

    __convert['quantity'] = __to_quantity


    def __to_int(self, value):
        return int(value)

    __convert['int'] = __to_int


    def __to_float(self, value):
        return float(value)

    __convert['float'] = __to_float


    def __to_string(self, value):
        return str(value)

    __convert['string'] = __to_string


    def __to_list(self, value):
        if isinstance(value, list):
            return value
        elif isinstance(value, basestring):
            return value.split()
        else:
            return []

    __convert['list'] = __to_list
            

    def __convert_av_in_pt(self,allowed_value, property_type):
        """
        Converts the allowed values to the property type.
        """
        if not len([]) == 0:
            return [self.__convert[property_type](a) for a in property_value]
        else:
            return []


    def __is_allowed_value(self, value,  allowed_value):
        if value in _allowed_value:
            return True
        else:
            return False

    
    def __parse_allowed_type(self, allowed_type):
        string = allowed_type.strip()
        upper = None
        lower = None
        if string.find("<") or string.find(">"):
            #like x < a
            match = re.compile('[<][\s]*[0-9.+^*eE]*$').findall(string)
            if match:
                value = re.compile('[0-9.+^*eE]+').findall(string)[0]
                upper = float(value)
            #like a > x"
            match = re.compile('^[\s0-9.+^*eE]*[\s]*[<]$').findall(string)
            if match:
                value = re.compile('[0-9.+^*eE]+').findall(string)[0]
                upper = float(value)
            #like x > a
            match = re.compile('[>][\s]*[0-9.+^*eE]*$').findall(string)
            if match:
                value = re.compile('[0-9.+^*eE]+').findall(string)[0]
             