"""Parameter Parsing and Validation for Chassis Applications."""

import re


class ValidationError(Exception):
    """Raised when validation fails."""

    def __init__(self, message):
        self.message = message
        super(ValidationError, self).__init__()


class BaseValidator(object):
    """Base class for validators."""

    documentation = "Validated Parameter"
    message = "Validation failed."

    def __init__(self, message=None, documentation=None):
        self.request = None
        self.override_message = message
        self.override_documentation = documentation

    def get_documentation(self):
        """Retrieve the overridden or default documentation string."""

        if self.override_documentation:
            return self.override_documentation
        else:
            return self.documentation

    def get_message(self):
        """Retrieve the overridden or default error message string."""

        if self.override_message:
            return self.override_message
        else:
            return self.message

    def fail(self):
        """Raise a validation error with the messsage."""

        raise ValidationError(self.get_message())

    def validate(self, unused_value):
        """Override this to implement validation logic.

        Return the validated value or call self.fail() to raise a
        ValidationError.
        """
        raise NotImplementedError


class Boolean(BaseValidator):
    """Validates Boolean inputs for truthiness."""

    truthy = ['true', '1', 'yes', 't', 'y', True, 1]
    falsy = ['false', '0', 'no', 'f', 'n', False, 0]

    documentation = "Truthy value: Prefered `true` or `false`"
    message = "Valid boolean required"

    def validate(self, value):
        if isinstance(value, basestring):
            value = value.lower()

        if value in self.truthy:
            return True

        if value in self.falsy:
            return False

        self.fail()


class String(BaseValidator):
    """Validates strings with optional length requirements."""

    documentation = "String."
    message = "Valid string required."

    def __init__(self, min_length=None, max_length=None,
                 message=None, documentation=None):
        self.min_length = min_length
        self.max_length = max_length

        # TODO: Make custom messages based on parameters.

        super(String, self).__init__(message=message,
                                     documentation=documentation)

    def validate(self, value):
        if not isinstance(value, basestring):
            self.fail()

        length = len(value)

        if self.min_length is not None:
            if length < self.min_length:
                self.fail()

        if self.max_length is not None:
            if length > self.max_length:
                self.fail()

        return value


class Regex(BaseValidator):
    """Validates a string agains a regular expression."""

    def __init__(self, regex, message=None, documentation=None):
        self.pattern = re.compile(regex)

        super(Regex, self).__init__(message=message,
                                    documentation=documentation)

    def validate(self, value):
        match = self.pattern.match(value)

        if match and value == match.group():
            return value

        self.fail()


class Integer(BaseValidator):
    """Validates integers with optional length requirements."""

    def __init__(self, minimum=None, maximum=None,
                 message=None, documentation=None):

        self.minimum = minimum
        self.maximum = maximum

        super(Integer, self).__init__(message=message,
                                      documentation=documentation)

    def validate(self, value):

        if isinstance(value, basestring):
            try:
                value = int(value)
            except ValueError:
                self.fail()
        else:
            if value != int(value):
                self.fail()

        if self.minimum is not None and value < self.minimum:
            self.fail()

        if self.maximum is not None and value > self.maximum:
            self.fail()

        return value


class Number(BaseValidator):
    """Validates floating point numbers with optional length requirements."""

    def __init__(self, minimum=None, maximum=None,
                 message=None, documentation=None):

        self.minimum = minimum
        self.maximum = maximum

        super(Number, self).__init__(message=message,
                                     documentation=documentation)

    def validate(self, value):
        try:
            value = float(value)
        except ValueError:
            self.fail()

        if self.minimum is not None and value < self.minimum:
            self.fail()

        if self.maximum is not None and value > self.maximum:
            self.fail()

        return value

@@ -3339,21 +3339,20 @@
 %0A%0Aclass 
-Integ
+Numb
 er(BaseV
@@ -3383,13 +3383,27 @@
 tes 
-integ
+floating point numb
 ers 
@@ -3626,13 +3626,12 @@
 per(
-Integ
+Numb
 er, 
@@ -3654,33 +3654,32 @@
 essage=message,%0A
- 
                 
@@ -3764,55 +3764,8 @@
 e):%0A
-%0A        if isinstance(value, basestring):%0A    
     
@@ -3765,36 +3765,32 @@
 ):%0A        try:%0A
-    
             valu
@@ -3793,18 +3793,20 @@
 value = 
-in
+floa
 t(value)
@@ -3810,28 +3810,24 @@
 ue)%0A        
-   
- 
 except Value
@@ -3837,90 +3837,8 @@
 or:%0A
-                self.fail()%0A        else:%0A            if value != int(value):%0A    
     
@@ -4061,35 +4061,29 @@
 %0A%0Aclass 
-Number(BaseValidato
+Integer(Numbe
 r):%0A    
@@ -4095,35 +4095,21 @@
 lidates 
-floating point numb
+integ
 ers with
@@ -4260,99 +4260,109 @@
    s
-elf.
+uper(Integer, self).__init__(
 minimum
- = 
+=
 minimum
-%0A        self.
+, 
 maximum
- = 
+=
 maximum
-%0A%0A        super(Number, self).__init__(
+,%0A                                      
 mess
@@ -4366,32 +4366,33 @@
 essage=message,%0A
+ 
                 
@@ -4469,32 +4469,33 @@
 lf, value):%0A
+%0A
         
 try:%0A       
@@ -4482,33 +4482,57 @@
         
-try:%0A    
+# Do the Number validation first%0A
         
 value = 
@@ -4519,32 +4519,38 @@
 rst%0A        
+float_
 value = 
 float(value)
@@ -4533,37 +4533,61 @@
 oat_value = 
-flo
+super(Integer, self).valid
 at
+e
 (value)%0A
         exce
@@ -4570,32 +4570,33 @@
 date(value)%0A
+%0A
         
 except Value
@@ -4587,199 +4587,116 @@
     
-except ValueError:%0A            self.fail()%0A%0A        if self.minimum is not None and value %3C self.minimum:%0A            self.fail()%0A%0A        if self.maximum is not None and value %3E self.maximum
+int_value = int(float_value)%0A%0A        if int_value == float_value:%0A            return int_value%0A        else
 :%0A  
@@ -4717,30 +4717,8 @@
 .fail()%0A
-%0A        return value%0A

Refactor Integer Validator to extend Number validator to re-use logic.