using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Resources;
using System.Security;
using System.Text;
using System.Text.RegularExpressions;

// This demo targets .NET Framework 4.0. A number of validation attributes that exist in .NET Framework 4.5 cannot be used.
// That is why we have created our own counterparts of these attributes for this demo.
// If your application targets .NET Framework 4.5, use default validation attributes.
// If your application targets .NET Framework 4.0, you can copy and use these attributes or use DevExpress Validation Fluent API instead.

namespace DevExpress.DataAnnotations {
    public abstract class RegexAttributeBase : DataTypeAttribute {
        protected const RegexOptions DefaultRegexOptions = RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase;

        readonly Regex regex;

        public RegexAttributeBase(string regex, string defaultErrorMessage, DataType dataType)
            : this(new Regex(regex, DefaultRegexOptions), defaultErrorMessage, dataType) {
        }
        public RegexAttributeBase(Regex regex, string defaultErrorMessage, DataType dataType)
            : base(dataType) {
            this.regex = (Regex)regex;
            this.ErrorMessage = defaultErrorMessage;
        }
        public sealed override bool IsValid(object value) {
            if(value == null)
                return true;
            string input = value as string;
            return input != null && regex.Match(input).Length > 0;
        }
    }
    public sealed class ZipCodeAttribute : RegexAttributeBase {
        static Regex regex = new Regex(@"^[0-9][0-9][0-9][0-9][0-9]$", DefaultRegexOptions);
        const string Message = "The {0} field is not a valid ZIP code.";
        public ZipCodeAttribute()
            : base(regex, Message, DataType.Url) {
        }
    }
    public sealed class CreditCardAttribute : DataTypeAttribute {
        const string Message = "The {0} field is not a valid credit card number.";
        public CreditCardAttribute()
            : base(DataType.Custom) {
            this.ErrorMessage = Message;
        }
        public override bool IsValid(object value) {
            if(value == null)
                return true;
            string stringValue = value as string;
            if(stringValue == null)
                return false;
            stringValue = stringValue.Replace("-", "").Replace(" ", "");
            int number = 0;
            bool oddEvenFlag = false;
            foreach(char ch in stringValue.Reverse()) {
                if(ch < '0' || ch > '9')
                    return false;
                int digitValue = (ch - '0') * (oddEvenFlag ? 2 : 1);
                oddEvenFlag = !oddEvenFlag;
                while(digitValue > 0) {
                    number += digitValue % 10;
                    digitValue = digitValue / 10;
                }
            }
            return (number % 10) == 0;
        }
    }
}