"""
This module includes ways to describe input data of checkers
to provide meta-information, so we can automatically provide
some XML/JSON/CLI interface to the checkers. Other usages
may include auto-documentation of input data, providing
validation of input data, etc
"""

class Property(object):
	"""Describes some property (object field) of an entity"""
	def __init__(self, name, type, create_default=lambda: None):
		self.name = name
		self.type = type
		self.create_default = create_default

class PropertyType(object):
	"""Base class for property types"""
	pass

class PropertyTypeString(PropertyType):
	"""String property type, maps to Python string"""
	pass

class PropertyTypeList(PropertyType):
	"""List property type, maps to Python list"""
	def __init__(self, item_property_type):
		"""Parameters:
		- item_property_type - object of PropertyType class or its subclass"""
		self.item_property_type = item_property_type

class PropertyTypeEntity(PropertyType):
	"""Entity property type, maps to subclass of Entity class"""
	def __init__(self, entity_type):
		"""Parameters:
		- entity_type - subclass of Entity class
		"""
		self.entity_type = entity_type

class Entity(object):
	"""Entity is a simple properties-only class with input data for checker"""

	"""List of properties of an entity. Redefine in child classes"""
	properties = [
	]

	def __init__(self, *args, **kwargs):
		set_properties = set()

		# set properties passed as positional arguments
		properties = self.properties[:]
		properties.reverse()
		for arg in args:
			prop_name = properties.pop().name
			setattr(self, prop_name, arg)
			set_properties.add(prop_name)

		# set properties passed as named arguments
		for k, v in kwargs.iteritems():
			setattr(self, k, v)
			set_properties.add(k)

		# populate properties that were not passed to constructor
		# with default values
		all_properties_by_name = {}
		for prop in self.properties:
			all_properties_by_name[prop.name] = prop
		for prop_name in set(all_properties_by_name.keys()) - set_properties:
			prop = all_properties_by_name[prop_name]
			setattr(self, prop.name, prop.create_default())

	def __repr__(self):
		"""Nice representation of an object for simple debugging"""
		return '%s(%s)' % (
			self.__class__.__name__,
			", ".join([
				"%s=%r"  % (prop, getattr(self, prop),)
				for prop in dir(self)
				if not prop.startswith('_') and not prop == 'properties'
			])
		)
