context: // Copyright (c) .NET Foundation. All rights reserved.
  // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.Globalization;
  using System.Text;
  using Microsoft.AspNet.Mvc.Extensions;
  using Microsoft.AspNet.Mvc.ModelBinding;
  using Microsoft.AspNet.Mvc.Rendering.Internal;
  using Microsoft.Framework.DependencyInjection;
  using Microsoft.Framework.Internal;
  
  namespace Microsoft.AspNet.Mvc.Rendering
  {
      public static class DefaultEditorTemplates
      {
          private const string HtmlAttributeKey = "htmlAttributes";
  
          public static string BooleanTemplate(IHtmlHelper htmlHelper)
          {
              bool? value = null;
              if (htmlHelper.ViewData.Model != null)
              {
                  value = Convert.ToBoolean(htmlHelper.ViewData.Model, CultureInfo.InvariantCulture);
              }
  
              return htmlHelper.ViewData.ModelMetadata.IsNullableValueType ?
                  BooleanTemplateDropDownList(htmlHelper, value) :
                  BooleanTemplateCheckbox(htmlHelper, value ?? false);
          }
  
          private static string BooleanTemplateCheckbox(IHtmlHelper htmlHelper, bool value)
          {
              return htmlHelper.CheckBox(
                  expression: null,
                  isChecked: value,
                  htmlAttributes: CreateHtmlAttributes(htmlHelper, "check-box"))
                      .ToString();
          }
  
          private static string BooleanTemplateDropDownList(IHtmlHelper htmlHelper, bool? value)
          {
              return htmlHelper.DropDownList(
                  expression: null,
                  selectList: DefaultDisplayTemplates.TriStateValues(value),
                  optionLabel: null,
                  htmlAttributes: CreateHtmlAttributes(htmlHelper, "list-box tri-state"))
                      .ToString();
          }
  
          public static string CollectionTemplate(IHtmlHelper htmlHelper)
          {
              var viewData = htmlHelper.ViewData;
              var model = viewData.Model;
              if (model == null)
              {
                  return string.Empty;
              }
  
              var collection = model as IEnumerable;
              if (collection == null)
              {
                  // Only way we could reach here is if user passed templateName: "Collection" to an Editor() overload.
                  throw new InvalidOperationException(Resources.FormatTemplates_TypeMustImplementIEnumerable(
                      "Collection", model.GetType().FullName, typeof(IEnumerable).FullName));
              }
  
              var elementMetadata = htmlHelper.ViewData.ModelMetadata.ElementMetadata;
              Debug.Assert(elementMetadata != null);
              var typeInCollectionIsNullableValueType = elementMetadata.IsNullableValueType;
  
              var serviceProvider = htmlHelper.ViewContext.HttpContext.RequestServices;
              var metadataProvider = serviceProvider.GetRequiredService<IModelMetadataProvider>();
  
              // Use typeof(string) instead of typeof(object) for IEnumerable collections. Neither type is Nullable<T>.
              if (elementMetadata.ModelType == typeof(object))
              {
                  elementMetadata = metadataProvider.GetMetadataForType(typeof(string));
              }
  
              var oldPrefix = viewData.TemplateInfo.HtmlFieldPrefix;
              try
              {
                  viewData.TemplateInfo.HtmlFieldPrefix = string.Empty;
  
                  var fieldNameBase = oldPrefix;
                  var result = new StringBuilder();
                  var viewEngine = serviceProvider.GetRequiredService<ICompositeViewEngine>();
  
                  var index = 0;
                  foreach (var item in collection)
                  {
                      var itemMetadata = elementMetadata;
                      if (item != null && !typeInCollectionIsNullableValueType)
                      {
                          itemMetadata = metadataProvider.GetMetadataForType(item.GetType());
                      }
  
                      var modelExplorer = new ModelExplorer(
                          metadataProvider,
                          container: htmlHelper.ViewData.ModelExplorer,
                          metadata: itemMetadata,
                          model: item);
                      var fieldName = string.Format(CultureInfo.InvariantCulture, "{0}[{1}]", fieldNameBase, index++);
  
                      var templateBuilder = new TemplateBuilder(
                          viewEngine,
                          htmlHelper.ViewContext,
                          htmlHelper.ViewData,
                          modelExplorer,
                          htmlFieldName: fieldName,
                          templateName: null,
                          readOnly: false,
                          additionalViewData: null);
  
                      var output = templateBuilder.Build();
                      result.Append(output);
                  }
  
                  return result.ToString();
              }
              finally
              {
                  viewData.TemplateInfo.HtmlFieldPrefix = oldPrefix;
              }
          }
  
          public static string DecimalTemplate(IHtmlHelper htmlHelper)
          {
              if (htmlHelper.ViewData.TemplateInfo.FormattedModelValue == htmlHelper.ViewData.Model)
              {
                  htmlHelper.ViewData.TemplateInfo.FormattedModelValue =
                      string.Format(CultureInfo.CurrentCulture, "{0:0.00}", htmlHelper.ViewData.Model);
              }
  
              return StringTemplate(htmlHelper);
          }
  
          public static string HiddenInputTemplate(IHtmlHelper htmlHelper)
          {
              var viewData = htmlHelper.ViewData;
              var model = viewData.Model;
  
              string result;
              if (viewData.ModelMetadata.HideSurroundingHtml)
              {
                  result = string.Empty;
              }
              else
              {
                  result = DefaultDisplayTemplates.StringTemplate(htmlHelper);
              }
  
              // Special-case opaque values and arbitrary binary data.
              var modelAsByteArray = model as byte[];
              if (modelAsByteArray != null)
              {
                  model = Convert.ToBase64String(modelAsByteArray);
              }
  
              var htmlAttributesObject = viewData[HtmlAttributeKey];
              var hiddenResult = htmlHelper.Hidden(expression: null, value: model, htmlAttributes: htmlAttributesObject);
              result += hiddenResult.ToString();
  
              return result;
          }
  
          private static IDictionary<string, object> CreateHtmlAttributes(
              IHtmlHelper htmlHelper,
              string className,
              string inputType = null)
          {
              var htmlAttributesObject = htmlHelper.ViewData[HtmlAttributeKey];
              if (htmlAttributesObject != null)
              {
                  return MergeHtmlAttributes(htmlAttributesObject, className, inputType);
              }
  
              var htmlAttributes = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
              {
                  { "class", className }
              };
  
              if (inputType != null)
              {
                  htmlAttributes.Add("type", inputType);
              }
  
              return htmlAttributes;
          }
  
          private static IDictionary<string, object> MergeHtmlAttributes(
              object htmlAttributesObject,
              string className,
              string inputType)
          {
              var htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributesObject);
  
              object htmlClassObject;
              if (htmlAttributes.TryGetValue("class", out htmlClassObject))
              {
                  var htmlClassName = htmlClassObject.ToString() + " " + className;
                  htmlAttributes["class"] = htmlClassName;
              }
              else
              {
                  htmlAttributes.Add("class", className);
              }
  
              // The input type from the provided htmlAttributes overrides the inputType parameter.
              if (inputType != null && !htmlAttributes.ContainsKey("type"))
              {
                  htmlAttributes.Add("type", inputType);
              }
  
              return htmlAttributes;
          }
  
          public static string MultilineTemplate(IHtmlHelper htmlHelper)
          {
              var htmlString = htmlHelper.TextArea(
                  expression: string.Empty,
                  value: htmlHelper.ViewContext.ViewData.TemplateInfo.FormattedModelValue.ToString(),
                  rows: 0,
                  columns: 0,
                  htmlAttributes: CreateHtmlAttributes(htmlHelper, "text-box multi-line"));
              return htmlString.ToString();
          }
  
          public static string ObjectTemplate(IHtmlHelper htmlHelper)
          {
              var viewData = htmlHelper.ViewData;
              var templateInfo = viewData.TemplateInfo;
              var modelExplorer = viewData.ModelExplorer;
              var builder = new StringBuilder();
  
              if (templateInfo.TemplateDepth > 1)
              {
                  if (modelExplorer.Model == null)
                  {
                      return modelExplorer.Metadata.NullDisplayText;
                  }
  
                  var text = modelExplorer.GetSimpleDisplayText();
                  if (modelExplorer.Metadata.HtmlEncode)
                  {
  
gt:                     text = htmlHelper.Encode(text);