
class ProcessorError(ValueError):
    def __init__(self, arg0):
        ValueError.__init__(self, arg0)

class property_processor(object):
    """ processes a value befor it is passed as a property """
    def __init__(self):
        pass
    
    def __add__(self, other):
        if isinstance(other, property_processor):
            return __compound_property_processor__([self, other])
        elif other == None:
            return self
        else:
            raise TypeError("Cannot add %s to property_processor " % type(other))
    
    def __iadd__(self, other):
        C = self.__and__(other)
        self = C
        return self
    
    
    def __call__(self, value, obj = None):
        return self.process(value, obj)
    
    def process(self, value, obj =None):
        return value

    def __repr__(self):
        return "<Property Processor >"

class __compound_property_processor__(property_processor):
    """ compound property processor class """
    def __init__(self, processors = []):
        self.__sub_processors = processors

    def __add__(self, other):
        if isinstance(other, __compound_property_processor__):
            return __compound_property_processor__(self.__sub_processors + other.__sub_processors)
        elif isinstance(other, property_processor):
            return __compound_property_processor__(self.__sub_processors + [other])
        else:
            raise TypeError("Cannot add %s to property_processor" % type(other))
    
    def __iadd__(self, other):
        if isinstance(other, __compound_property_processor__):
            self.__sub_processors += other.__sub_processors
            return self
        elif isinstance(other, property_processor):
            self.__sub_processors += [other]
            return self
        else:
            raise TypeError("Cannot add %s to property_processor" % type(other))
    
    def process(self, value, obj = None):
        """ processes the value """ 
        v = value
        for R in self.__sub_processors:
            v = R.process(self, value, obj)
        return v

    def __repr__(self):
        S = "< Compound Property Processor:"
        for i in self.__sub_processors:
            S += "   %s" % i.__repr__() 
        S += ">"
        return S

class processor_typecast(property_processor):
    """ restrict the type or types the argument can have, and tries a typecast where possible """
    def __init__(self, cast_type):
        if not isinstance(cast_type, type):
            raise TypeError("cast_type argument %s in TypeCast Processor should be of type 'type'" % cast_type)
        self.cast_type = cast_type
    
    def process(self, value, obj = None):
        if isinstance(value, self.cast_type):
            return value
        else:
            return self.cast_type(value)
        
    def __repr__(self):
        S = "<Type Cast Processor: %s >" % self.cast_type.__name__


def processor_int():
    return processor_typecast(int)
def processor_float():
    return processor_typecast(float)
def processtor_str():
    return processor_typecast(str)

class processor_int_round(property_processor):
    """ rounds a number to the nearest integer"""
    
    def process(self, value, obj = None):
        return int(round(value))
        
    def __repr__(self):
        S = "<Int Round Property Restriction >"

class processor_range(property_processor):
    """ brings a number to within a certain range """
    def __init__(self, lower = None, upper = None):
        self.lower = lower
        self.upper = upper
        if lower == None and upper == None:
            raise ValueError("Range Processor should have an upper or lower limit")
        if not upper == None and not lower == None:
            if lower > upper:
                raise ValueError("lower limit should be smaller than upper limit in Range Processor")
            

    def process(self, value, obj = None):
        if not self.lower == None:
            if value < self.lower: return self.lower
        if not self.upper == None:
            if value > self.upper: return self.upper
        return value
        
    def __repr__(self):
        S = "<Range Property Restriction: [%s, %s] >" % (str(self.lower), str(upper))
        
