context: using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Collections.Specialized;
  using System.ComponentModel.DataAnnotations;
  using System.Globalization;
  using System.Linq;
  using System.Reflection;
  using System.Runtime.Serialization;
  using System.Web.Http;
  using System.Web.Http.Description;
  using System.Xml.Serialization;
  using Newtonsoft.Json;
  
  namespace Chat.RestAPI.Areas.HelpPage.ModelDescriptions
  {
  	/// <summary>
  	/// Generates model descriptions for given types.
  	/// </summary>
  	public class ModelDescriptionGenerator
  	{
  		// Modify this to support more data annotation attributes.
  		private readonly IDictionary<Type, Func<object, string>> AnnotationTextGenerator = new Dictionary<Type, Func<object, string>>
  		{
  			{ typeof(RequiredAttribute), a => "Required" },
  			{ typeof(RangeAttribute), a =>
  				{
  					RangeAttribute range = (RangeAttribute)a;
  					return String.Format(CultureInfo.CurrentCulture, "Range: inclusive between {0} and {1}", range.Minimum, range.Maximum);
  				}
  			},
  			{ typeof(MaxLengthAttribute), a =>
  				{
  					MaxLengthAttribute maxLength = (MaxLengthAttribute)a;
  					return String.Format(CultureInfo.CurrentCulture, "Max length: {0}", maxLength.Length);
  				}
  			},
  			{ typeof(MinLengthAttribute), a =>
  				{
  					MinLengthAttribute minLength = (MinLengthAttribute)a;
  					return String.Format(CultureInfo.CurrentCulture, "Min length: {0}", minLength.Length);
  				}
  			},
  			{ typeof(StringLengthAttribute), a =>
  				{
  					StringLengthAttribute strLength = (StringLengthAttribute)a;
  					return String.Format(CultureInfo.CurrentCulture, "String length: inclusive between {0} and {1}", strLength.MinimumLength, strLength.MaximumLength);
  				}
  			},
  			{ typeof(DataTypeAttribute), a =>
  				{
  					DataTypeAttribute dataType = (DataTypeAttribute)a;
  					return String.Format(CultureInfo.CurrentCulture, "Data type: {0}", dataType.CustomDataType ?? dataType.DataType.ToString());
  				}
  			},
  			{ typeof(RegularExpressionAttribute), a =>
  				{
  					RegularExpressionAttribute regularExpression = (RegularExpressionAttribute)a;
  					return String.Format(CultureInfo.CurrentCulture, "Matching regular expression pattern: {0}", regularExpression.Pattern);
  				}
  			},
  		};
  
  		// Modify this to add more default documentations.
  		private readonly IDictionary<Type, string> DefaultTypeDocumentation = new Dictionary<Type, string>
  		{
  			{ typeof(Int16), "integer" },
  			{ typeof(Int32), "integer" },
  			{ typeof(Int64), "integer" },
  			{ typeof(UInt16), "unsigned integer" },
  			{ typeof(UInt32), "unsigned integer" },
  			{ typeof(UInt64), "unsigned integer" },
  			{ typeof(Byte), "byte" },
  			{ typeof(Char), "character" },
  			{ typeof(SByte), "signed byte" },
  			{ typeof(Uri), "URI" },
  			{ typeof(Single), "decimal number" },
  			{ typeof(Double), "decimal number" },
  			{ typeof(Decimal), "decimal number" },
  			{ typeof(String), "string" },
  			{ typeof(Guid), "globally unique identifier" },
  			{ typeof(TimeSpan), "time interval" },
  			{ typeof(DateTime), "date" },
  			{ typeof(DateTimeOffset), "date" },
  			{ typeof(Boolean), "boolean" },
  		};
  
  		private Lazy<IModelDocumentationProvider> _documentationProvider;
  
  		public ModelDescriptionGenerator(HttpConfiguration config)
  		{
  			if (config == null)
  			{
  				throw new ArgumentNullException("config");
  			}
  
  			_documentationProvider = new Lazy<IModelDocumentationProvider>(() => config.Services.GetDocumentationProvider() as IModelDocumentationProvider);
  			GeneratedModels = new Dictionary<string, ModelDescription>(StringComparer.OrdinalIgnoreCase);
  		}
  
  		public Dictionary<string, ModelDescription> GeneratedModels { get; private set; }
  
  		private IModelDocumentationProvider DocumentationProvider
  		{
  			get
  			{
  				return _documentationProvider.Value;
  			}
  		}
  
  		public ModelDescription GetOrCreateModelDescription(Type modelType)
  		{
  			if (modelType == null)
  			{
  				throw new ArgumentNullException("modelType");
  			}
  
  			if (modelType.IsGenericType && typeof (Cqrs.WebApi.HttpResponseMessage<>).IsAssignableFrom(modelType.GetGenericTypeDefinition()))
  				modelType = modelType.GetGenericArguments().Single();
  
  			Type underlyingType = Nullable.GetUnderlyingType(modelType);
  			if (underlyingType != null)
  			{
  				modelType = underlyingType;
  			}
  
  			ModelDescription modelDescription;
  			string modelName = ModelNameHelper.GetModelName(modelType);
  			if (GeneratedModels.TryGetValue(modelName, out modelDescription))
  			{
  				if (modelType != modelDescription.ModelType)
  				{
  					throw new InvalidOperationException(
  						String.Format(
  							CultureInfo.CurrentCulture,
  							"A model description could not be created. Duplicate model name '{0}' was found for types '{1}' and '{2}'. " +
  							"Use the [ModelName] attribute to change the model name for at least one of the types so that it has a unique name.",
  							modelName,
  							modelDescription.ModelType.FullName,
  							modelType.FullName));
  				}
  
  				return modelDescription;
  			}
  
  			if (DefaultTypeDocumentation.ContainsKey(modelType))
  			{
  				return GenerateSimpleTypeModelDescription(modelType);
  			}
  
  			if (modelType.IsEnum)
  			{
  				return GenerateEnumTypeModelDescription(modelType);
  			}
  
  			if (modelType.IsGenericType)
  			{
  				Type[] genericArguments = modelType.GetGenericArguments();
  
  				if (genericArguments.Length == 1)
  				{
  					Type enumerableType = typeof(IEnumerable<>).MakeGenericType(genericArguments);
  					if (enumerableType.IsAssignableFrom(modelType))
  					{
  						return GenerateCollectionModelDescription(modelType, genericArguments[0]);
  					}
  				}
  				if (genericArguments.Length == 2)
  				{
  					Type dictionaryType = typeof(IDictionary<,>).MakeGenericType(genericArguments);
  					if (dictionaryType.IsAssignableFrom(modelType))
  					{
  						return GenerateDictionaryModelDescription(modelType, genericArguments[0], genericArguments[1]);
  					}
  
  					Type keyValuePairType = typeof(KeyValuePair<,>).MakeGenericType(genericArguments);
  					if (keyValuePairType.IsAssignableFrom(modelType))
  					{
  						return GenerateKeyValuePairModelDescription(modelType, genericArguments[0], genericArguments[1]);
  					}
  				}
  			}
  
  			if (modelType.IsArray)
  			{
  				Type elementType = modelType.GetElementType();
  				return GenerateCollectionModelDescription(modelType, elementType);
  			}
  
  			if (modelType == typeof(NameValueCollection))
  			{
  				return GenerateDictionaryModelDescription(modelType, typeof(string), typeof(string));
  			}
  
  			if (typeof(IDictionary).IsAssignableFrom(modelType))
  			{
  				return GenerateDictionaryModelDescription(modelType, typeof(object), typeof(object));
  			}
  
  			if (typeof(IEnumerable).IsAssignableFrom(modelType))
  			{
  				return GenerateCollectionModelDescription(modelType, typeof(object));
  			}
  
  			return GenerateComplexTypeModelDescription(modelType);
  		}
  
  		// Change this to provide different name for the member.
  		private static string GetMemberName(MemberInfo member, bool hasDataContractAttribute)
  		{
  			JsonPropertyAttribute jsonProperty = member.GetCustomAttribute<JsonPropertyAttribute>();
  			if (jsonProperty != null && !String.IsNullOrEmpty(jsonProperty.PropertyName))
  			{
  				return jsonProperty.PropertyName;
  			}
  
  			if (hasDataContractAttribute)
  			{
  				DataMemberAttribute dataMember = member.GetCustomAttribute<DataMemberAttribute>();
  				if (dataMember != null && !String.IsNullOrEmpty(dataMember.Name))
  				{
  					return dataMember.Name;
  				}
  			}
  
  			return member.Name;
  		}
  
  		private static bool ShouldDisplayMember(MemberInfo member, bool hasDataContractAttribute)
  		{
  			JsonIgnoreAttribute jsonIgnore = member.GetCustomAttribute<JsonIgnoreAttribute>();
  			XmlIgnoreAttribute xmlIgnore = member.GetCustomAttribute<XmlIgnoreAttribute>();
  			IgnoreDataMemberAttribute ignoreDataMember = member.GetCustomAttribute<IgnoreDataMemberAttribute>();
  			NonSerializedAttribute nonSerialized = member.GetCustomAttribute<NonSerializedAttribute>();
  			ApiExplorerSettingsAttribute apiExplorerSetting = member.GetCustomAttribute<ApiExplorerSettingsAttribute>();
  
  			bool hasMemberAttribute = member.DeclaringType.IsEnum ?
  				member.GetCustomAttribute<EnumMemberAttribute>() != null :
  				member.GetCustomAttribute<DataMemberAttribute>() != null;
  
  			// Display member only if all the followings are true:
  			// no JsonIgnoreAttribute
  			// no XmlIgnoreAttribute
  			// no IgnoreDataMemberAttribute
  			// no NonSerializedAttribute
  			// no ApiExplorerSettingsAttribute with IgnoreApi set to true
  			// no DataContractAttribute without DataMemberAttribute or EnumMemberAttribute
  			return jsonIgnore == null &&
  				xmlIgnore == null &&
  				ignoreDataMember == null &&
  				nonSerialized == null &&
  				(apiExplorerSetting == null || !apiExplorerSetting.IgnoreApi) &&
  				(!hasDataContractAttribute || hasMemberAttribute);
  		}
  
  		private string CreateDefaultDocumentation(Type type)
  		{
  			string documentation;
  			if (DefaultTypeDocumentation.TryGetValue(type, out documentation))
  			{
  				return documentation;
  			}
  			if (DocumentationProvider != null)
  			{
  				documentation = DocumentationProvider.GetDocumentation(type);
  			}
  
  			return documentation;
  		}
  
  		private void GenerateAnnotations(MemberInfo property, ParameterDescription propertyModel)
  		{
  			List<ParameterAnnotation> annotations = new List<ParameterAnnotation>();
  
  			IEnumerable<Attribute> attributes = property.GetCustomAttributes();
  			foreach (Attribute attribute in attributes)
  			{
  				Func<object, string> textGenerator;
  				if (AnnotationTextGenerator.TryGetValue(attribute.GetType(), out textGenerator))
  				{
  					annotations.Add(
  						new ParameterAnnotation
  						{
  							AnnotationAttribute = attribute,
  							Documentation = textGenerator(attribute)
  						});
  				}
  			}
  
  			// Rearrange the annotations
  			annotations.Sort((x, y) =>
  			{
  				// Special-case RequiredAttribute so that it shows up on top
  				if (x.AnnotationAttribute is RequiredAttribute)
  				{
  					return -1;
  				}
  				if (y.AnnotationAttribute is RequiredAttribute)
  				{
  					return 1;
  				}
  
  				// Sort the rest based on alphabetic order of the documentation
  				return String.Compare(x.Documentation, y.Documentation, StringComparison.OrdinalIgnoreCase);
  			});
  
  			foreach (ParameterAnnotation annotation in annotations)
  			{
  				propertyModel.Annotations.Add(annotation);
  			}
  		}
  
  		private CollectionModelDescription GenerateCollectionModelDescription(Type modelType, Type elementType)
  		{
  			ModelDescription collectionModelDescription = GetOrCreateModelDescription(elementType);
  			if (collectionModelDescription != null)
  			{
  				return new CollectionModelDescription
  				{
  					Name = ModelNameHelper.GetModelName(modelType),
  					ModelType = modelType,
  					ElementDescription = collectionModelDescription
  				};
  			}
  
  			return null;
  		}
  
  		private ModelDescription GenerateComplexTypeModelDescription(Type modelType)
  		{
  			ComplexTypeModelDescription complexModelDescription = new ComplexTypeModelDescription
  			{
  				Name = ModelNameHelper.GetModelName(modelType),
  				ModelType = modelType,
  				Documentation = CreateDefaultDocumentation(modelType)
  			};
  
  			GeneratedModels.Add(complexModelDescription.Name, complexModelDescription);
  			bool hasDataContractAttribute = modelType.GetCustomAttribute<DataContractAttribute>() != null;
  
  			PropertyInfo[] properties = modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
  			foreach (PropertyInfo property in properties)
  			{
  				if (ShouldDisplayMember(property, hasDataContractAttribute))
  				{
  					ParameterDescription propertyModel = new ParameterDescription
  					{
  						Name = GetMemberName(property, hasDataContractAttribute)
  					};
  
  					if (DocumentationProvider != null)
  					{
  						propertyModel.Documentation = DocumentationProvider.GetDocumentation(property);
  					}
  
  					GenerateAnnotations(property, propertyModel);
  					complexModelDescription.Properties.Add(propertyModel);
  					propertyModel.TypeDescription = GetOrCreateModelDescription(property.PropertyType);
  				}
  			}
  
  			FieldInfo[] fields = modelType.GetFields(BindingFlags.Public | BindingFlags.Instance);
  			foreach (FieldInfo field in fields)
  			{
  				if (ShouldDisplayMember(field, hasDataContractAttribute))
  				{
  					ParameterDescription propertyModel = new ParameterDescription
  					{
  						Name = GetMemberName(field, hasDataContractAttribute)
  					};
  
  
gt: 					if (DocumentationProvider != null)