class Konvalidator(object):
	'''
	The abstract base class for validators.

	Override validate_value for validation logic.
	Override convert_vlaue for konversion logic.
	
	Use ValidationError for validation exceptions
	Use KonversionError for konversion exceptions
	
	Return True for successful validation
	Return value for successful konversion

	'''

	def __call__ (self, value):
		'''
		Validates and konverts user input.
		
		Validation must happen before konversion because validation
		is always based on raw user input.

		Konversion is strictly for post-processing after input has been validated.
		'''
		
		self.validate(value)
		value = self.convert(value)
		return value

	def convert (self, value):
		'''
		The interface for invoking the konverter

		'''

		converted_value = self.convert_value(value)
		return converted_value
	
	def convert_value (self, value):
		'''
		Transform a value to the desired form.
		
		Return konverted value on success, exception on failure.

		'''
		return value
	
	def validate (self, value):
		'''
		The interface for invoking the validator

		'''

		self.validate_value(value)
		return value
	
	def validate_value (self, value):
		'''
		Check a value is of the desired form.
		
		Override this method, return True on success, raise exception on failture.
		
		'''
		return True

class Or(Konvalidator):
	'''
	Given a list of validators, return the first that succeeds.
	Raise last exception if none succeed.
	
	'''
	
	def __init__(self, *validators):
		self.validators = validators
	
	def __call__(self, value):
		last_exception = None
		for validator in self.validators:
			try:
				valid_value = validator(value)
				return valid_value
			except KonvalError as e:
				last_exception = e

		raise last_exception


class And(Konvalidator):
	''' All validators must succeed. If a group specific error message is supplied
		it will be raised, otherwise the first error raised is thrown. 
	'''

	def __init__(self, *validators, **options):
		self.validators = validators
		self.error_message = options.get('error_message')

	def __call__(self, value):
		current_value = value
		for validator in self.validators:
			try:
				current_value = validator(current_value)
			except KonvalError as e:
				if self.error_message:
					raise KonvalError(self.error_message.format(value=current_value))
				else:
					raise e
		return current_value

class If(Konvalidator):
	''' If condition is satisfied, then validate / convert '''

	def __init__(self, condition, validator):
		self.condition = condition
		self.validator = validator

	def __call__(self, value):
		if self.condition:
			return self.validator(value)
		return value

class IfElse(Konvalidator):
	''' If first validator fails, try second one. '''

	def __init__(self, validator, other_validator):
		self.validator = validator
		self.other_validator = other_validator

	def __call__(self, value):
		try:
			return self.validator(value)
		except KonvalError:
			return self.other_validator(value)

class IfThen(Konvalidator):
	''' If first validator succeeds, run second validator '''

	def __init__(self, predicate, secondary):
		self.predicate = predicate
		self.secondary = secondary

	def __call__(self, value):
		try:
			value = self.predicate(value)
		except KonvalError:
			return value
		return self.secondary(value)


class Default(Konvalidator):
	'''
	Return a default value if the supplied validator fails.
	This validator never throws, always returns a value.
	
	'''
	
	def __init__(self, validator, default):
		self.default = default
		self.validator = validator

	def __call__(self, value):
		try:
			valid_value = self.validator(value)
			return valid_value
		except KonvalError:
			return self.default

class Constant(Konvalidator):
	'''
	Return the same value for all inputs.

	'''

	def __init__(self, constant_value):
		self.constant_value = constant_value
	
	def __call__(self, value):
		return self.constant_value

class Result(object):

	def __init__(self):
		self.errors = {}
		self.successes = {}

	def add_error(self, name, message):
		if name not in self.errors.keys():
			self.errors[name] = []
		self.errors[name].append(message)

	def add_success(self, name, value):
		self.successes[name] = value

	def get_errors(self):
		return dict((k, v) for k, v in self.errors.iteritems() if v != [])

	def get_valid(self):
		return dict((k, v) for k, v in self.successes.iteritems())

	def is_valid(self):
		return self.get_errors() == {}

	def get_value(self, name):
		try:
			return self.successes[name]
		except KeyError:
			return None

	def write(self, obj):
		for name, value in self.get_valid().iteritems():
			setattr(obj, name, value)

class Schema(object):

	def __init__(self):
		self.fields = {}

	def add_field(self, name, validator, required=False, default=None, error=None):
		self.fields[unicode(name)] = {u'validator': validator, u'required': required, u'default': default, u'error': error}

	def remove_field(self, name):
		if name in self.fields:
			del self.fields[name]

	def validate(self, value_dict):
		self.result = Result()
		for name, schema in self.fields.iteritems():
			if name in value_dict and value_dict[name]:
				try:
					valid_value = schema[u'validator'](value_dict[name])
					self.result.add_success(name, valid_value)
				except KonvalError as e:
					self.result.add_error(name, e.message)
			elif schema[u'required']:
				self.result.add_error(name, '%s is a required value' % name)
			elif schema[u'default'] is not None:
				self.result.add_success(name, schema[u'default'])
		return self.result

def validate(schema, data):
	result = schema.validate(data)
	return result

def quick(validator, value):
	try:
		validator(value)
		return True
	except KonvalError:
		return False

def once(validator, value):
	try:
		return validator(value)
	except KonvalError as e:
		return e.message


class KonvalError(Exception): pass

class ValidationError(KonvalError): pass

class KonversionError(KonvalError): pass

from . import canonicals, containers, dates, numbers, strings, types, vocabulary