# Modified validators for FormKit

from FormKit.BaseValidatorClasses import InvalidField   # this is also referred to in other FormKit modules,
                                                        # so we will just leave it that way
import re, datetime

########################################################
# Base class. This is edited from FormKit
class ModVC:

    def __init__(self, error=None):
        '''
        You can customize the error message of derived classes by modifying the default value,
        and that of instances, obviously, by passing a suitable argument.
        '''
        self._error = error

    def error(self):
        if self._error:
            return self._error
        else:
            return 'This %s instance has no valid error message.' % self.__class__.__name__

    def _attemptConvert(self, value):
        # core logic: validate the input (value)
        #   if validation passes, run the value through convert and return it
        #   else, raise an error with a message that can be set for the base class, or for an instance,
        #   or if need be even in _validate itself.
        #   this is again changed from the FormKit model

        # change: if value is a string (it initially is, but other validators might have
        # converted it already to something else), strip white space
        # If the stripped value is empty, we will return None but not raise an error. This means all
        # derived validators will pass through empty values, as long as they don't override
        # _attemptConvert. This then needs to be done in NotEmpty.

        if value and type(value) is str:
            value = value.strip()

        if not value:
            return None

        if not self._validate(value):
            raise InvalidField, self.error()
        else:
            return self._convert(value)

    def _convert(self, value):
        """By default, all validators do no conversion... they just return what they are given"""
        # overwrite this method in a subclass to create a converter
        return value

    def _validate(self, value):
        """Return True if valid"""
        # overwrite this method in a subclass to create a validator
        return True


######################################################
# Now, on to some real validators.

_dateRe = re.compile('\D')
class IsDate(ModVC):
    '''
    this is rather limited. Try to dig up some code that guesses data from various formats.
    '''
    def __init__(self, error='Please input a valid date (e.g., 2004-12-24)'):
        ModVC.__init__(self, error)

    def _process(self, value):
        try:
            spl = _dateRe.split(value)[:3]
            year, month, day = [int(s) for s in spl]
            d = datetime.date(year, month, day)     # if we get here, the user entered what seems a valid date
            self._processed = str(d)
        except:
            self._processed = None

    def _validate(self, value):
        self._process(value)
        return self._processed

    def _convert(self, value):
        return self._processed



#####################################################################################
# These guys must accept interspersed whitespace. Whitespace only is taken care of by the
# strip that is now in the base class.

_isAND = re.compile('[^A-Za-z0-9\-\s]')
def isAlphaNumericDash(raw):
    if _isAND.search(raw): return False;
    return True;

_isAN = re.compile('[^A-Za-z0-9\s]')
def isAlphaNumeric(raw):
    if _isAN.search(raw): return False;
    return True;

_isAlpha = re.compile('[^A-Za-z]')
def isAlpha(raw):
    if _isAlpha.search(raw): return False;
    return True;

class AlphaNumericDash(ModVC):
    def __init__(self, error="Use only letters, digits, and dashes"):
        ModVC.__init__(self, error)

    def _validate(self, value):
        return isAlphaNumericDash(value)


class AlphaNumeric(ModVC):
    def __init__(self, error="Use only letters and digits"):
        ModVC.__init__(self, error)

    def _validate(self, value):
        return isAlphaNumeric(value)


class Alpha(ModVC):
    def __init__(self, error="Use only letters"):
        ModVC.__init__(self, error)

    def _validate(self, value):
        return isAlpha(value)




##################################################################################
# these classes are taken from the original module content but modified according
# to the new base class logic.

class NotEmpty(ModVC):
    def __init__(self, error='Please enter some value'):
        ModVC.__init__(self, error)

    def _attemptConvert(self, value):
        if value and type(value) is str:
            value = value.strip()
        if value:
            return value
        else:
            raise InvalidField, self.error()



# these are just boring NotEmpty clones.
class MustChoose(NotEmpty):
    def __init__(self, error='Please choose at least one value.'):
        ModVC.__init__(self, error)


class RequiredField(NotEmpty):
    def __init__(self, error='This field is required.'):
        ModVC.__init__(self, error)


class FileUploaded(NotEmpty):
    '''To requires user to upload a file with a FileField.
       There should indeed be slightly more sophistication here. this was just a notempty clone.
    '''
    def __init__(self, error='Please upload a file.'):
        ModVC.__init__(self, error)


# finally, something useful.
class MaxLength(ModVC):
    '''
    It seems that this is actually not necessary - it should always be possible to
    build this restriction into the form itself, which would then save one round trip.
    Or how about textarea?
    '''
    def __init__(self, maxlength, error=None):
        self._maxlength = maxlength
        try:
            theError = error % maxlength
        except:
            theError = "Enter a value no more than %i characters long" % maxlength
        ModVC.__init__(self, theError)

    def _validate(self, value):
        try:
            return len(str(value)) <= self._maxlength
        except TypeError:
            return False


class MinLength(ModVC):

    def __init__(self, minlength, error=None):
        self._minlength = minlength
        try:
            theError = error % minlength
        except:
            theError = "Enter a value at least %i characters long" % minlength
        ModVC.__init__(self, theError)

    def _validate(self, value):
        try:
            return len(str(value)) >= self._minlength
        except TypeError:
            return False


class ExactLength(ModVC):

    def __init__(self, length, error=None):
        self._length = length
        try:
            theError = error % length
        except:
            theError = "Enter a value exactly %i characters long" % length
        ModVC.__init__(self, theError)

    def _validate(self, value):
        try:
            return len(str(value)) == self._length
        except TypeError:
            return False


class ConvertToInt(ModVC):
    def __init__(self, error="Please enter an integer value"):
        ModVC.__init__(self, error)

    def _validate(self, value):
        try:
            int(value)
            return True
        except ValueError:
            return False

    def _convert(self, value):
        return int(value)


class UpperAllChars(ModVC):
    '''Converts all characters in a string into caps'''
    def _convert(self, value):
        return str( value ).upper()


class CapitalizeAllWords(ModVC):
    '''Capitalizes each word in a string'''
    def _convert(self, value):
        bits = str(value).strip().split()
        caps = [b.capitalize() for b in bits]
        return ' '.join(caps)


class DollarAmountAsString(ModVC):
    """ Checks that the value is a valid dollar amount
    (i.e. "10.95", "$1.50", "1", etc.) and converts the value to a string,
    stripping the '$' if present. """
    # this class donated by Jason Hildebrand (jason@remotequote.ca)
    # disfigured and mutilated by M. Palmer

    def __init__(self, error="This field must be a dollar amount."):
        ModVC.__init__(self, error)


    def _validate(self,value):
        if len(value) > 0 and value[0] == "$":
            value = value[1:]
        try:
            p = float(value)
        except:
            return False
        return True

    def _convert(self, value):
        if len(value) > 0 and value[0] == "$":
            value = value[1:]
        p = float(value)
        return "%.2f" % p


class EmailAddress(ModVC):
    """ Checks the value to make sure it at least looks like a valid email address.
    In normal mode it checks using a regular expression.  If you pass checkdns=1 to
    the constructor, it will check that the domain exists.  This requires the
    pydns module from http://pydns.sf.net (Debian users: apt-get install python-dns) """
    # this class donated by Jason Hildebrand (jason@remotequote.ca)
    # Note that some features are dependent on the external library
    # disfigured and mutilated by M. Palmer


    def __init__(self,checkdns=False, error="Please enter a valid email address."):
        ModVC.__init__(self, error)
        self._checkdns = checkdns

    def _validate(self,value):
        if self._checkdns:
            try:
                import DNS
                i = value.find('@')
                if i != -1:
                    domain = value[i+1:]
                    if domain:
                        DNS.ParseResolvConf()
                        r = DNS.DnsRequest(name=domain,qtype='ANY')
                        a = r.req()
                        if a.answers:
                            return True
                return False
            except:
                pass
        import re
        return re.match('[^@]+@[^\.]+(\.[^\.]+)+',value)

