using System;
using Csla.Properties;
using System.Text.RegularExpressions;
using System.Reflection;
namespace Csla.Validation
{
  /// 
  /// Implements common business rules.
  /// 
  public static class CommonRules
  {
    #region StringRequired
    /// 
    /// Rule ensuring a string value contains one or more
    /// characters.
    /// 
    /// Object containing the data to validate
    /// Arguments parameter specifying the name of the string
    /// property to validate
    ///  if the rule is broken
    /// 
    /// This implementation uses late binding, and will only work
    /// against string property values.
    /// 
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")]
    public static bool StringRequired(object target, RuleArgs e)
    {
      string value = (string)Utilities.CallByName(
        target, e.PropertyName, CallType.Get);
      if (string.IsNullOrEmpty(value))
      {
        e.Description = string.Format(Resources.StringRequiredRule, e.PropertyName);
        return false;
      }
      return true;
    }
    #endregion
    #region StringMaxLength
    /// 
    /// Rule ensuring a string value doesn't exceed
    /// a specified length.
    /// 
    /// Object containing the data to validate
    /// Arguments parameter specifying the name of the string
    /// property to validate
    ///  if the rule is broken
    /// 
    /// This implementation uses late binding, and will only work
    /// against string property values.
    /// 
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")]
    public static bool StringMaxLength(
      object target, RuleArgs e)
    {
      int max = ((MaxLengthRuleArgs)e).MaxLength;
      string value = (string)Utilities.CallByName(
        target, e.PropertyName, CallType.Get);
      if (!String.IsNullOrEmpty(value) && (value.Length > max))
      {
        e.Description = String.Format(
          Resources.StringMaxLengthRule,
          e.PropertyName, max.ToString());
        return false;
      }
      return true;
    }
    /// 
    /// Custom  object required by the
    ///  rule method.
    /// 
    public class MaxLengthRuleArgs : RuleArgs
    {
      private int _maxLength;
      /// 
      /// Get the max length for the string.
      /// 
      public int MaxLength
      {
        get { return _maxLength; }
      }
      /// 
      /// Create a new object.
      /// 
      /// Name of the property to validate.
      /// Max length of characters allowed.
      public MaxLengthRuleArgs(
        string propertyName, int maxLength)
        : base(propertyName)
      {
        _maxLength = maxLength;
      }
      /// 
      /// Return a string representation of the object.
      /// 
      public override string ToString()
      {
        return base.ToString() + "?maxLength=" + _maxLength.ToString();
      }
    }
    #endregion
    #region IntegerMaxValue
    /// 
    /// Rule ensuring an integer value doesn't exceed
    /// a specified value.
    /// 
    /// Object containing the data to validate.
    /// Arguments parameter specifying the name of the
    /// property to validate.
    ///  if the rule is broken.
    public static bool IntegerMaxValue(object target, RuleArgs e)
    {
      int max = ((IntegerMaxValueRuleArgs)e).MaxValue;
      int value = (int)Utilities.CallByName(target, e.PropertyName, CallType.Get);
      if (value > max)
      {
        e.Description = String.Format(Resources.MaxValueRule,
          e.PropertyName, max.ToString());
        return false;
      }
      return true;
    }
    /// 
    /// Custom  object required by the
    ///  rule method.
    /// 
    public class IntegerMaxValueRuleArgs : RuleArgs
    {
      private int _maxValue;
      /// 
      /// Get the max value for the property.
      /// 
      public int MaxValue
      {
        get { return _maxValue; }
      }
      /// 
      /// Create a new object.
      /// 
      /// Name of the property.
      /// Maximum allowed value for the property.
      public IntegerMaxValueRuleArgs(string propertyName, int maxValue)
        : base(propertyName)
      {
        _maxValue = maxValue;
      }
      /// 
      /// Return a string representation of the object.
      /// 
      public override string ToString()
      {
        return base.ToString() + "?maxValue=" + _maxValue.ToString();
      }
    }
    #endregion
    #region IntegerMinValue
    /// 
    /// Rule ensuring an integer value doesn't go below
    /// a specified value.
    /// 
    /// Object containing the data to validate.
    /// Arguments parameter specifying the name of the
    /// property to validate.
    ///  if the rule is broken.
    public static bool IntegerMinValue(object target, RuleArgs e)
    {
      int min = ((IntegerMinValueRuleArgs)e).MinValue;
      int value = (int)Utilities.CallByName(target, e.PropertyName, CallType.Get);
      if (value < min)
      {
        e.Description = String.Format(Resources.MinValueRule,
          e.PropertyName, min.ToString());
        return false;
      }
      return true;
    }
    /// 
    /// Custom  object required by the
    ///  rule method.
    /// 
    public class IntegerMinValueRuleArgs : RuleArgs
    {
      private int _minValue;
      /// 
      /// Get the min value for the property.
      /// 
      public int MinValue
      {
        get { return _minValue; }
      }
      /// 
      /// Create a new object.
      /// 
      /// Name of the property.
      /// Minimum allowed value for the property.
      public IntegerMinValueRuleArgs(string propertyName, int minValue)
        : base(propertyName)
      {
        _minValue = minValue;
      }
      /// 
      /// Return a string representation of the object.
      /// 
      public override string ToString()
      {
        return base.ToString() + "?minValue=" + _minValue.ToString();
      }
    }
    #endregion
    #region MaxValue
    /// 
    /// Rule ensuring that a numeric value
    /// doesn't exceed a specified maximum.
    /// 
    /// Type of the property to validate.
    /// Object containing value to validate.
    /// Arguments variable specifying the
    /// name of the property to validate, along with the max
    /// allowed value.
    public static bool MaxValue(object target, RuleArgs e) where T : IComparable
    {
      PropertyInfo pi = target.GetType().GetProperty(e.PropertyName);
      T value = (T)pi.GetValue(target, null);
      T max = ((MaxValueRuleArgs)e).MaxValue;
      int result = value.CompareTo(max);
      if (result >= 1)
      {
        e.Description = string.Format(Resources.MaxValueRule,
          e.PropertyName, max.ToString());
        return false;
      }
      else
        return true;
    }
    /// 
    /// Custom  object required by the
    ///  rule method.
    /// 
    /// Type of the property to validate.
    public class MaxValueRuleArgs : RuleArgs
    {
      T _maxValue = default(T);
      /// 
      /// Get the max value for the property.
      /// 
      public T MaxValue
      {
        get { return _maxValue; }
      }
      /// 
      /// Create a new object.
      /// 
      /// Name of the property.
      /// Maximum allowed value for the property.
      public MaxValueRuleArgs(string propertyName, T maxValue)
        : base(propertyName)
      {
        _maxValue = maxValue;
      }
      /// 
      /// Returns a string representation of the object.
      /// 
      public override string ToString()
      {
        return base.ToString() + "?maxValue=" + _maxValue.ToString();
      }
    }
    #endregion
    #region MinValue
    /// 
    /// Rule ensuring that a numeric value
    /// doesn't exceed a specified minimum.
    /// 
    /// Type of the property to validate.
    /// Object containing value to validate.
    /// Arguments variable specifying the
    /// name of the property to validate, along with the min
    /// allowed value.
    public static bool MinValue(object target, RuleArgs e) where T : IComparable
    {
      PropertyInfo pi = target.GetType().GetProperty(e.PropertyName);
      T value = (T)pi.GetValue(target, null);
      T min = ((MinValueRuleArgs)e).MinValue;
      int result = value.CompareTo(min);
      if (result <= -1)
      {
        e.Description = string.Format(Resources.MinValueRule,
          e.PropertyName, min.ToString());
        return false;
      }
      else
        return true;
    }
    /// 
    /// Custom  object required by the
    ///  rule method.
    /// 
    /// Type of the property to validate.
    public class MinValueRuleArgs : RuleArgs
    {
      T _minValue = default(T);
      /// 
      /// Get the min value for the property.
      /// 
      public T MinValue
      {
        get { return _minValue; }
      }
      /// 
      /// Create a new object.
      /// 
      /// Name of the property.
      /// Minimum allowed value for the property.
      public MinValueRuleArgs(string propertyName, T minValue)
        : base(propertyName)
      {
        _minValue = minValue;
      }
      /// 
      /// Returns a string representation of the object.
      /// 
      public override string ToString()
      {
        return base.ToString() + "?minValue=" + _minValue.ToString();
      }
    }
    #endregion
    #region RegEx
    /// 
    /// Rule that checks to make sure a value
    /// matches a given regex pattern.
    /// 
    /// Object containing the data to validate
    /// RegExRuleArgs parameter specifying the name of the 
    /// property to validate and the regex pattern.
    /// False if the rule is broken
    /// 
    /// This implementation uses late binding.
    /// 
    public static bool RegExMatch(object target, RuleArgs e)
    {
      Regex rx = ((RegExRuleArgs)e).RegEx;
      if (!rx.IsMatch(Utilities.CallByName(target, e.PropertyName, CallType.Get).ToString()))
      {
        e.Description = String.Format(Resources.RegExMatchRule, e.PropertyName);
        return false;
      }
      else
        return true;
    }
    /// 
    /// List of built-in regex patterns.
    /// 
    public enum RegExPatterns
    {
      /// 
      /// US Social Security number pattern.
      /// 
      SSN,
      /// 
      /// Email address pattern.
      /// 
      Email
    }
    /// 
    /// Custom  object required by the
    ///  rule method.
    /// 
    public class RegExRuleArgs : RuleArgs
    {
      Regex _regEx;
      /// 
      /// The  object used to validate
      /// the property.
      /// 
      public Regex RegEx
      {
        get { return _regEx; }
      }
      /// 
      /// Creates a new object.
      /// 
      /// Name of the property to validate.
      /// Built-in regex pattern to use.
      public RegExRuleArgs(string propertyName, RegExPatterns pattern)
        :
        base(propertyName)
      {
        _regEx = new Regex(GetPattern(pattern));
      }
      /// 
      /// Creates a new object.
      /// 
      /// Name of the property to validate.
      /// Custom regex pattern to use.
      public RegExRuleArgs(string propertyName, string pattern)
        :
        base(propertyName)
      {
        _regEx = new Regex(pattern);
      }
      /// 
      /// Creates a new object.
      /// 
      /// Name of the property to validate.
      ///  object to use.
      public RegExRuleArgs(string propertyName, System.Text.RegularExpressions.Regex regEx)
        :
        base(propertyName)
      {
        _regEx = regEx;
      }
      /// f
      /// Returns a string representation of the object.
      /// 
      public override string ToString()
      {
        return base.ToString() + "?regex=" + _regEx.ToString();
      }
      /// 
      /// Returns the specified built-in regex pattern.
      /// 
      /// Pattern to return.
      public static string GetPattern(RegExPatterns pattern)
      {
        switch (pattern)
        {
          case RegExPatterns.SSN:
            return @"^\d{3}-\d{2}-\d{4}$";
          case RegExPatterns.Email:
            return @"^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$";
          default:
            return string.Empty;
        }
      }
    }
    #endregion
  }
}