context: #region KotikoBlog Header License
  
  // // Solution: KotikoBlog
  // // Project: KotikoBlog.Backend
  // //
  // // This file is included in the KotikoBlog solution.
  // //
  // // File created on 14/01/2016   15:11
  // //
  // // File Modified on 14/01/2016/   15:11
  // 
  // // Permission is hereby granted, free of charge, to any person obtaining a copy
  // // of this software and associated documentation files (the "Software"), to deal
  // // in the Software without restriction, including without limitation the rights
  // // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  // // copies of the Software, and to permit persons to whom the Software is
  // // furnished to do so, subject to the following conditions:
  // //
  // // The above copyright notice and this permission notice shall be included in all
  // // copies or substantial portions of the Software.
  // //
  // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  // // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  // // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  // // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  // // SOFTWARE.
  
  #endregion
  
  #region
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Collections.Specialized;
  using System.ComponentModel.DataAnnotations;
  using System.Globalization;
  using System.Reflection;
  using System.Runtime.Serialization;
  using System.Web.Http;
  using System.Web.Http.Description;
  using System.Xml.Serialization;
  using Newtonsoft.Json;
  
  #endregion
  
  namespace KotikoBlog.Backend.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 =>
                  {
                      var range = (RangeAttribute) a;
                      return string.Format(CultureInfo.CurrentCulture, "Range: inclusive between {0} and {1}",
                          range.Minimum, range.Maximum);
                  }
              },
              {
                  typeof (MaxLengthAttribute), a =>
                  {
                      var maxLength = (MaxLengthAttribute) a;
                      return string.Format(CultureInfo.CurrentCulture, "Max length: {0}", maxLength.Length);
                  }
              },
              {
                  typeof (MinLengthAttribute), a =>
                  {
                      var minLength = (MinLengthAttribute) a;
                      return string.Format(CultureInfo.CurrentCulture, "Min length: {0}", minLength.Length);
                  }
              },
              {
                  typeof (StringLengthAttribute), a =>
                  {
                      var strLength = (StringLengthAttribute) a;
                      return string.Format(CultureInfo.CurrentCulture, "String length: inclusive between {0} and {1}",
                          strLength.MinimumLength, strLength.MaximumLength);
                  }
              },
              {
                  typeof (DataTypeAttribute), a =>
                  {
                      var dataType = (DataTypeAttribute) a;
                      return string.Format(CultureInfo.CurrentCulture, "Data type: {0}",
                          dataType.CustomDataType ?? dataType.DataType.ToString());
                  }
              },
              {
                  typeof (RegularExpressionAttribute), a =>
                  {
                      var 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 (short), "integer"},
              {typeof (int), "integer"},
              {typeof (long), "integer"},
              {typeof (ushort), "unsigned integer"},
              {typeof (uint), "unsigned integer"},
              {typeof (ulong), "unsigned integer"},
              {typeof (byte), "byte"},
              {typeof (char), "character"},
              {typeof (sbyte), "signed byte"},
              {typeof (Uri), "URI"},
              {typeof (float), "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 (bool), "boolean"}
          };
  
          private readonly 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 IModelDocumentationProvider DocumentationProvider
          {
              get { return _documentationProvider.Value; }
          }
  
          public ModelDescription GetOrCreateModelDescription(Type modelType)
          {
              if (modelType == null)
              {
                  throw new ArgumentNullException("modelType");
              }
  
              var underlyingType = Nullable.GetUnderlyingType(modelType);
              if (underlyingType != null)
              {
                  modelType = underlyingType;
              }
  
              ModelDescription modelDescription;
              var 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)
              {
                  var genericArguments = modelType.GetGenericArguments();
  
                  if (genericArguments.Length == 1)
                  {
                      var enumerableType = typeof (IEnumerable<>).MakeGenericType(genericArguments);
                      if (enumerableType.IsAssignableFrom(modelType))
                      {
                          return GenerateCollectionModelDescription(modelType, genericArguments[0]);
                      }
                  }
                  if (genericArguments.Length == 2)
                  {
                      var dictionaryType = typeof (IDictionary<,>).MakeGenericType(genericArguments);
                      if (dictionaryType.IsAssignableFrom(modelType))
                      {
                          return GenerateDictionaryModelDescription(modelType, genericArguments[0], genericArguments[1]);
                      }
  
                      var keyValuePairType = typeof (KeyValuePair<,>).MakeGenericType(genericArguments);
                      if (keyValuePairType.IsAssignableFrom(modelType))
                      {
                          return GenerateKeyValuePairModelDescription(modelType, genericArguments[0], genericArguments[1]);
                      }
                  }
              }
  
              if (modelType.IsArray)
              {
                  var 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)
          {
              var jsonProperty = member.GetCustomAttribute<JsonPropertyAttribute>();
              if (jsonProperty != null && !string.IsNullOrEmpty(jsonProperty.PropertyName))
              {
                  return jsonProperty.PropertyName;
              }
  
              if (hasDataContractAttribute)
              {
                  var 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)
          {
              var jsonIgnore = member.GetCustomAttribute<JsonIgnoreAttribute>();
              var xmlIgnore = member.GetCustomAttribute<XmlIgnoreAttribute>();
              var ignoreDataMember = member.GetCustomAttribute<IgnoreDataMemberAttribute>();
              var nonSerialized = member.GetCustomAttribute<NonSerializedAttribute>();
              var apiExplorerSetting = member.GetCustomAttribute<ApiExplorerSettingsAttribute>();
  
              var 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)
          {
              var annotations = new List<ParameterAnnotation>();
  
              var attributes = property.GetCustomAttributes();
              foreach (var attribute in attributes)
              {
                  Func<object, string> textGenerator;
                  if (AnnotationTextGenerator.TryGetValue(attribute.GetType(), out textGenerator))
                  {
                      annotations.Add(
                          new ParameterAnnotation
                          {
                              AnnotationAttribute = attribute,
  
gt:                             Documentation = textGenerator(attribute)