//===============================================================================
// Microsoft patterns & practices
// Web Service Software Factory
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Configuration;
using System.ComponentModel;
using System.Collections;

namespace Microsoft.Practices.SoftwareFactories.ServiceFactory.Editors.Grid
{
    /// <summary/>
    public static class Validation
    {
        /// <summary>
        /// Executes the <see cref="ConfigurationValidatorAttribute"/> attributes on the
        /// data members binded to a DataGridView and adds error text labels to the cells
        /// </summary>
        /// <param name="grid">The grid to validate on</param>
        /// <param name="site">The site.</param>
        /// <param name="binding">The binding.</param>
        /// <returns>
        /// Returns <code>true</code> if the validation succeeded, <code>false</code> otherwise
        /// </returns>
        public static bool ExecuteConfigurationValidatorAttributes(DataGridView grid, ISite site, IBindingStrategy binding)
        {
            if (grid == null)
                throw new ArgumentNullException("grid");

            if (binding == null)
                throw new ArgumentNullException("binding");
            
            bool isValid = true;

            // Executes validators in each cell and add an ErrorText
            foreach (DataGridViewRow gridRow in grid.Rows)
            {
                object boundItem = binding.GetBoundItem(gridRow);
                if (boundItem == null)
                    continue;

                Type itemType = boundItem.GetType();
                foreach (DataGridViewCell gridCell in gridRow.Cells)
                {
                    isValid &= ExecuteConfigurationValidatorAttributes(gridCell, itemType, site);
                }
            }

            return isValid;
        }

        /// <summary>
        /// Executes the <see cref="ConfigurationValidatorAttribute"/> attributes on the
        /// data member binded to a DataGridViewCell and adds error text label to the cell
        /// </summary>
        /// <param name="gridCell">The cell to validate</param>
        /// <param name="itemType"></param>
        /// <param name="site"></param>
        /// <returns></returns>
        public static bool ExecuteConfigurationValidatorAttributes(DataGridViewCell gridCell, Type itemType, ISite site)
        {
            if (gridCell == null)
                throw new ArgumentNullException("gridCell");

            if (itemType == null)
                throw new ArgumentNullException("itemType");
            
            if (gridCell.OwningColumn.DataPropertyName == null)
                return true;

            gridCell.ErrorText = null;

            PropertyInfo itemProperty = itemType.GetProperty(gridCell.OwningColumn.DataPropertyName);
            if (itemProperty == null)
                return true;

            // Executes a type converter associated with the property
            TypeConverterAttribute converterAttribute = Attribute.GetCustomAttribute(itemProperty, typeof(TypeConverterAttribute)) as TypeConverterAttribute;
            if (converterAttribute != null)
            {
                Type converterType = Type.GetType(converterAttribute.ConverterTypeName, true);
                TypeConverter converter = (TypeConverter)Activator.CreateInstance(converterType);

                try
                {
                    gridCell.Value = converter.ConvertFrom(gridCell.Value);
                }
                catch (Exception ex)
                {
                    AppendErrorText(gridCell, ex.Message);
                    return false;
                }
            }

            bool isValid = true;

            foreach (ConfigurationValidatorAttribute validatorAttr in itemProperty.GetCustomAttributes(typeof(ConfigurationValidatorAttribute), true))
            {
                try
                {
                    ConfigurationValidatorBase validator = validatorAttr.ValidatorInstance;
                    IComponent validatorComponent = validator as IComponent;
                    if (validatorComponent != null)
                        validatorComponent.Site = site;
                    validator.Validate(gridCell.Value);
                }
                catch (Exception ex)
                {
                    AppendErrorText(gridCell, ex.Message);
                    isValid = false;
                }
            }

            return isValid;
        }

        /// <summary>
        /// Executes the configuration validator attributes.
        /// </summary>
        /// <param name="grid">The grid.</param>
        /// <param name="site">The site.</param>
        /// <returns></returns>
        public static bool ExecuteConfigurationValidatorAttributes(DataGridView grid, ISite site)
        {
            return ExecuteConfigurationValidatorAttributes(grid, site, new AutomaticBindingStrategy());
        }

        /// <summary>
        /// Checks the duplicates.
        /// </summary>
        /// <param name="grid">The grid.</param>
        /// <param name="column">The column.</param>
        /// <param name="errorMessage">The error message.</param>
        /// <returns></returns>
        public static bool CheckDuplicates(DataGridView grid, DataGridViewColumn column, string errorMessage)
        {
            return CheckDuplicates<object>(grid, column, errorMessage,
                delegate (DataGridViewRow row)
                {
                    return row.Cells[column.Index].Value;
                });
        }

        /// <summary>
        /// Checks the duplicates.
        /// </summary>
        /// <param name="grid">The grid.</param>
        /// <param name="column">The column.</param>
        /// <param name="errorMessage">The error message.</param>
        /// <param name="rowDataConverter">The row data converter.</param>
        /// <returns></returns>
        public static bool CheckDuplicates<T>(DataGridView grid, DataGridViewColumn column, string errorMessage, Converter<DataGridViewRow, T> rowDataConverter)
        {
            bool isValid = true;

            List<T> values = new List<T>();
            foreach (DataGridViewRow row in grid.Rows)
            {
                T rowValue = rowDataConverter(row);
                if (rowValue != null)
                {
                    if (values.Contains(rowValue))
                    {
                        AppendErrorText(row.Cells[column.Index], errorMessage);
                        isValid = false;
                    }
                    else
                    {
                        values.Add(rowValue);
                    }
                }
            }

            return isValid;
        }

        /// <summary>
        /// Appends the error text.
        /// </summary>
        /// <param name="cell">The cell.</param>
        /// <param name="message">The message.</param>
        public static void AppendErrorText(DataGridViewCell cell, string message)
        {
            if (cell == null)
                throw new ArgumentNullException("cell");
            
            if (string.IsNullOrEmpty(cell.ErrorText))
                cell.ErrorText = message;
            else
                cell.ErrorText += Environment.NewLine + message;
        }
    }
}
