public static CodeStatement CreateToSourceAssignmentMethod(
			TranslateAttribute attr,
			CodeVariableDeclarationStatement toSource,
			PropertyInfo toSourceProperty, CodeParameterDeclarationExpression fromTargetParam,
			Func<Type, Type, TranslateAttribute> getTypesTranslateAttributeFn)
		{

			var fromTargetProperty = attr.TargetType.GetProperty(toSourceProperty.Name);
			var cantReadFromTarget = fromTargetProperty.GetGetMethod() == null;
			if (cantReadFromTarget)
			{
				return new CodeCommentStatement(string.Format("Skipping property 'to.{0}' because 'model.{1}' is write-only",
					toSourceProperty.Name, fromTargetProperty.Name));
			}
			var cantWriteToSource = toSourceProperty.GetSetMethod() == null;
			if (cantWriteToSource)
			{
				return new CodeCommentStatement(string.Format("Skipping property 'to.{0}' because 'to.{1}' is read-only",
					toSourceProperty.Name, toSourceProperty.Name));
			}

			//to[property.Name] = this[property.Name] e.g:
			//	to.Name = from.Name;
			if (fromTargetProperty.PropertyType.IsAssignableFrom(toSourceProperty.PropertyType))
			{
				return toSource.Assign(
					toSource.RefProperty(toSourceProperty.Name),
					fromTargetParam.Name.RefArgument().RefProperty(toSourceProperty.Name));
			}

			//to[property.Name] = from[property.PropertyType.Name].ToTarget() e.g:
			//to.Address = from.Address.ToTarget();

			var toSourcePropertyType = toSourceProperty.PropertyType;
			var fromTargetPropertyType = fromTargetProperty.PropertyType;
			var targetAttr = getTypesTranslateAttributeFn(toSourcePropertyType, fromTargetPropertyType);
			var useExtensionMethods = attr is TranslateExtensionAttribute;
			var isTargetTranslatableAlso = targetAttr != null;

			if (isTargetTranslatableAlso)
			{
				var toSourceMethodName = targetAttr.GetConvertToSourceMethodName();
				var method = useExtensionMethods
									? fromTargetParam.RefProperty(toSourceProperty.Name).Call(toSourceMethodName)
									: toSourcePropertyType.Call(toSourceMethodName, fromTargetParam.RefProperty(toSourceProperty.Name));
				return toSource.Assign(toSourceProperty.Name, method);
			}

			var fromSourceIsGenericList = toSourcePropertyType.IsGenericType && toSourcePropertyType.GetGenericTypeDefinition() == typeof(List<>);
			var toTargetIsGenericList = fromTargetPropertyType.IsGenericType && fromTargetPropertyType.GetGenericTypeDefinition() == typeof(List<>);
			var bothAreGenericLists = fromSourceIsGenericList && toTargetIsGenericList;

			if (bothAreGenericLists)
			{
				//to.PhoneNumbers = from.PhoneNumbers.ToPhoneNumbers();
				var propertyListItemTypeAttr = getTypesTranslateAttributeFn(toSourcePropertyType.GetGenericArguments()[0], fromTargetPropertyType.GetGenericArguments()[0]);
				var sourceIsTranslatable = propertyListItemTypeAttr != null;
				if (sourceIsTranslatable)
				{
					var toSourcesMethodName = propertyListItemTypeAttr.GetConvertToSourcesMethodName();
					var method = useExtensionMethods
					             		? fromTargetParam.RefProperty(fromTargetProperty.Name).Call(toSourcesMethodName)
					             		: propertyListItemTypeAttr.SourceType.Call(toSourcesMethodName, fromTargetParam.RefProperty(fromTargetProperty.Name));

					return toSource.RefProperty(toSourceProperty.Name).Assign(method);
				}
			}

			//to[property.Name] = this[property.Name].ToString() e.g:
			//	to.Name = from.Name;
			if (toSourceProperty.PropertyType == typeof(string)
				&& StringConverterUtils.CanCreateFromString(fromTargetProperty.PropertyType))
			{
				var fromTargetPropertyRef = fromTargetParam.Name.RefArgument().RefProperty(toSourceProperty.Name);
				return fromTargetPropertyRef.IfIsNotNull(
							toSource.Assign(
								toSource.RefProperty(toSourceProperty.Name),
								fromTargetPropertyRef.Call("ToString")));
			}

			// Converting 'System.Collections.Generic.List`1 PhoneNumbers' to 'System.Collections.Generic.List`1 PhoneNumbers' is unsupported
			return new CodeCommentStatement(string.Format("Converting '{0}.{1} {2}' to '{3}.{4} {5}' is unsupported"
				, toSourceProperty.PropertyType.Namespace, toSourceProperty.PropertyType.Name, toSourceProperty.Name
				, fromTargetProperty.PropertyType.Namespace, fromTargetProperty.PropertyType.Name, fromTargetProperty.Name));
		}