import inspect


def find_attributes(clazz):
	attrs = inspect.getmembers(clazz, lambda a: (not inspect.isroutine(a)))
	attrs = filter(lambda a: not (a[0].endswith('__')), attrs)
	attrs = filter(lambda a: a[0].startswith('_' + clazz.__name__), attrs)

	return attrs


def getter(original_class):
	def add_property(clazz):
		class_name = clazz.__name__

		for attr in find_attributes(clazz):
			original_attr_name = attr[0]  # _Foo__attr
			property_attr_name = attr[0].replace('_' + class_name + '__', '')  # attr

			# getter name:
			# 	get_Foo__attr
			getter_name = 'get' + original_attr_name

			# add func:
			# 	def get_Foo__attr(self): return self._Foo__attr
			setattr(clazz, getter_name, make_getter(original_attr_name))

			# add property func:
			# 	attr = property(get_Foo__attr)
			setattr(clazz, property_attr_name, property(getattr(clazz, getter_name)))

	def make_getter(name):
		def fg(self):
			return getattr(self, name)

		return fg

	add_property(original_class)
	return original_class


def setter(original_class):
	def add_property_setter(clazz):
		class_name = clazz.__name__

		for attr in find_attributes(clazz):
			original_attr_name = attr[0]  # _Foo__attr
			property_attr_name = attr[0].replace('_' + class_name + '__', '')  # attr

			# setter name:
			# 	set_Foo__attr
			setter_name = 'set' + original_attr_name
			getter_name = 'get' + original_attr_name

			if not hasattr(clazz, getter_name):
				getter(clazz)

			# add func:
			# 	def set_Foo__attr(self, v): self._Foo__attr = v
			setattr(clazz, setter_name, make_setter(original_attr_name))

			# add property func:
			# 	attr = property(get_Foo__attr, set_Foo__attr)
			setattr(clazz, property_attr_name, property(getattr(clazz, getter_name), getattr(clazz, setter_name)))

	def make_setter(name):
		def fs(self, v):
			setattr(self, name, v)

		return fs

	add_property_setter(original_class)
	return original_class


def setter_validator(original_class):
	def add_property_validation_setter(clazz):
		class_name = clazz.__name__

		for attr in find_attributes(clazz):
			original_attr_name = attr[0]  # _Foo__attr
			property_attr_name = attr[0].replace('_' + class_name + '__', '')  # attr

			# setter name:
			# 	set_Foo__attr
			setter_name = 'set' + original_attr_name
			getter_name = 'get' + original_attr_name

			if not hasattr(clazz, getter_name):
				getter(clazz)

			field_value = getattr(clazz, original_attr_name)

			# add func:
			# 	def set_Foo__attr(self, v):
			# 		if type(v) != CustomeType:
			# 			raise ValueError()
			#
			# 		self._Foo__attr = v
			#
			if isinstance(field_value, type(None)):
				setter(clazz)
			else:
				setattr(clazz, setter_name, make_validation_setter(original_attr_name, type(field_value)))

			# add property func:
			# 	attr = property(get_Foo__attr, set_Foo__attr)
			setattr(clazz, property_attr_name, property(getattr(clazz, getter_name), getattr(clazz, setter_name)))

	def make_validation_setter(name, custom_type):
		def fs(self, v):
			if type(v) != custom_type:
				raise ValueError('value must be ' + custom_type.__name__ + ' type.')

			setattr(self, name, v)

		return fs

	add_property_validation_setter(original_class)
	return original_class

import inspect


def find_attributes(clazz):
	attrs = inspect.getmembers(clazz, lambda a: (not inspect.isroutine(a)))
	attrs = filter(lambda a: not (a[0].endswith('__')), attrs)
	attrs = filter(lambda a: a[0].startswith('_' + clazz.__name__), attrs)

	return attrs


def getter(original_class):
	def add_property(clazz):
		class_name = clazz.__name__

		for attr in find_attributes(clazz):
			original_attr_name = attr[0]  # _Foo__attr
			property_attr_name = attr[0].replace('_' + class_name + '__', '')  # attr

			# getter name:
			# 	get_Foo__attr
			getter_name = 'get' + original_attr_name

			# add func:
			# 	def get_Foo__attr(self): return self._Foo__attr
			setattr(clazz, getter_name, make_getter(original_attr_name))

			# add property func:
			# 	attr = property(get_Foo__attr)
			setattr(clazz, property_attr_name, property(getattr(clazz, getter_name)))

	def make_getter(name):
		def fg(self):
			return getattr(self, name)

		return fg

	add_property(original_class)
	return original_class


def setter(original_class):
	def add_property_setter(clazz):
		class_name = clazz.__name__

		for attr in find_attributes(clazz):
			original_attr_name = attr[0]  # _Foo__attr
			property_attr_name = attr[0].replace('_' + class_name + '__', '')  # attr

			# setter name:
			# 	set_Foo__attr
			setter_name = 'set' + original_attr_name
			getter_name = 'get' + original_attr_name

			if not hasattr(clazz, getter_name):
				getter(clazz)

			# add func:
			# 	def set_Foo__attr(self, v): self._Foo__attr = v
			setattr(clazz, setter_name, make_setter(original_attr_name))

			# add property func:
			# 	attr = property(get_Foo__attr, set_Foo__attr)
			setattr(clazz, property_attr_name, property(getattr(clazz, getter_name), getattr(clazz, setter_name)))

	def make_setter(name):
		def fs(self, v):
			setattr(self, name, v)

		return fs

	add_property_setter(original_class)
	return original_class


def setter_validator(original_class):
	def add_property_validation_setter(clazz):
		class_name = clazz.__name__

		for attr in find_attributes(clazz):
			original_attr_name = attr[0]  # _Foo__attr
			property_attr_name = attr[0].replace('_' + class_name + '__', '')  # attr

			# setter name:
			# 	set_Foo__attr
			setter_name = 'set' + original_attr_name
			getter_name = 'get' + original_attr_name

			if not hasattr(clazz, getter_name):
				getter(clazz)

			field_value = getattr(clazz, original_attr_name)

			# add func:
			# 	def set_Foo__attr(self, v):
			# 		if type(v) != CustomeType:
			# 			raise ValueError()
			#
			# 		self._Foo__attr = v
			#
			if isinstance(field_value, type(None)):
				setter(clazz)
			else:
				setattr(clazz, setter_name, make_validation_setter(original_attr_name, type(field_value)))

			# add property func:
			# 	attr = property(get_Foo__attr, set_Foo__attr)
			setattr(clazz, property_attr_name, property(getattr(clazz, getter_name), getattr(clazz, setter_name)))

	def make_validation_setter(name, custom_type):
		def fs(self, v):
			if type(v) != custom_type:
				raise ValueError('value must be ' + custom_type.__name__ + ' type.')

			setattr(self, name, v)

		return fs

	add_property_validation_setter(original_class)
	return original_class

# _Foo__attr # attr # getter name: # 	get_Foo__attr # add func: # 	def get_Foo__attr(self): return self._Foo__attr # add property func: # 	attr = property(get_Foo__attr) # _Foo__attr # attr # setter name: # 	set_Foo__attr # add func: # 	def set_Foo__attr(self, v): self._Foo__attr = v # add property func: # 	attr = property(get_Foo__attr, set_Foo__attr) # _Foo__attr # attr # setter name: # 	set_Foo__attr # add func: # 	def set_Foo__attr(self, v): # 		if type(v) != CustomeType: # 			raise ValueError() # # 		self._Foo__attr = v # # add property func: # 	attr = property(get_Foo__attr, set_Foo__attr)