// 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.Generic;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace Microsoft.AspNetCore.Mvc.ViewFeatures
{
    /// <summary>
    /// Contract for a service providing validation attributes for expressions.
    /// </summary>
    public abstract class ValidationHtmlAttributeProvider
    {
        /// <summary>
        /// Adds validation-related HTML attributes to the <paramref name="attributes" /> if client validation is
        /// enabled.
        /// </summary>
        /// <param name="viewContext">A <see cref="ViewContext"/> instance for the current scope.</param>
        /// <param name="modelExplorer">The <see cref="ModelExplorer"/> for an expression.</param>
        /// <param name="attributes">
        /// The <see cref="Dictionary{TKey, TValue}"/> to receive the validation attributes. Maps the validation
        /// attribute names to their <see cref="string"/> values. Values must be HTML encoded before they are written
        /// to an HTML document or response.
        /// </param>
        /// <remarks>
        /// Adds nothing to <paramref name="attributes"/> if client-side validation is disabled.
        /// </remarks>
        public abstract void AddValidationAttributes(
            ViewContext viewContext,
            ModelExplorer modelExplorer,
            IDictionary<string, string> attributes);

        /// <summary>
        /// Adds validation-related HTML attributes to the <paramref name="attributes" /> if client validation is
        /// enabled and validation attributes have not yet been added for this <paramref name="expression"/> in the
        /// current &lt;form&gt;.
        /// </summary>
        /// <param name="viewContext">A <see cref="ViewContext"/> instance for the current scope.</param>
        /// <param name="modelExplorer">The <see cref="ModelExplorer"/> for the <paramref name="expression"/>.</param>
        /// <param name="expression">Expression name, relative to the current model.</param>
        /// <param name="attributes">
        /// The <see cref="Dictionary{TKey, TValue}"/> to receive the validation attributes. Maps the validation
        /// attribute names to their <see cref="string"/> values. Values must be HTML encoded before they are written
        /// to an HTML document or response.
        /// </param>
        /// <remarks>
        /// Tracks the <paramref name="expression"/> in the current <see cref="FormContext"/> to avoid generating
        /// duplicate validation attributes. That is, validation attributes are added only if no previous call has
        /// added them for a field with this name in the &lt;form&gt;.
        /// </remarks>
        public virtual void AddAndTrackValidationAttributes(
            ViewContext viewContext,
            ModelExplorer modelExplorer,
            string expression,
            IDictionary<string, string> attributes)
        {
            if (viewContext == null)
            {
                throw new ArgumentNullException(nameof(viewContext));
            }

            if (modelExplorer == null)
            {
                throw new ArgumentNullException(nameof(modelExplorer));
            }

            if (attributes == null)
            {
                throw new ArgumentNullException(nameof(attributes));
            }

            // Don't track fields when client-side validation is disabled.
            var formContext = viewContext.ClientValidationEnabled ? viewContext.FormContext : null;
            if (formContext == null)
            {
                return;
            }

            var fullName = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expression);
            if (formContext.RenderedField(fullName))
            {
                return;
            }

            formContext.RenderedField(fullName, true);

            AddValidationAttributes(viewContext, modelExplorer, attributes);
        }
    }
}