var Validator = Class.create();
Validator.REQUIRED = /.+/;
Validator.EMPTY_TRIM = /\S+/;
Validator.EMAIL = /^((\"[^\"\f\n\r\t\v\b]+\")|([\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+(\.[\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+)*))@((\[(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))\])|(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))|((([A-Za-z0-9\-])+\.)+[A-Za-z\-]+))$/;
Validator.INT = /^([-+]?[1-9][0-9]*|)$/
Validator.FLOAT = /^([-+]?[0-9]*\.?[0-9]*|)$/
Validator.NUMBER = /(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)/
Validator.NO_PUNCTUATION = /^[^().\/\*\^\?#!@$%+=,\"\'><~\[\]{}]+$/
Validator.NONZERO = /^-?[1-9][0-9]*/
Validator.ALPHANUMERIC = /^[a-zA-Z0-9]+$/
Validator.LETTERS_ONLY = /^[a-zA-Z]+$/
Validator.ZIPCODE = /^[1-9][0-9]{3}\s?[a-z]{2}$/i

Validator.DATE = function (s) {
    var re_dt = /^(\d{4})\-(\d{1,2})\-(\d{1,2})$/;
    
    // check format
    if (!re_dt.test(s)) {
        return false;
    }
    // check allowed ranges	
    if (RegExp.$3 > 31 || RegExp.$2 > 12)
        return false;
    // check number of day in month
    var d = new Date(RegExp.$1, Number(RegExp.$2 - 1), RegExp.$3);
    if (d.getMonth() != Number(RegExp.$2 - 1))
        return false;
    return true;
};

Validator.ACCOUNT = function (accNum) {
    var re_acc_postbank = /^[pP]? ?\d{1,7}$/i;
    var re_acc_bank = /^(\d{9,10}|\d{2,3}.\d{2}.\d{2}.\d{3})$/;

	// postbank can have min 3 digits & max 7 with an optional P in front of the account number	
	if((accNum.length <= 9  && (accNum.indexOf('p') == 0 || accNum.indexOf('P') == 0)) || accNum.length <= 8){

	    accNum = accNum.replace(/\./g, '');

		// checks either for a P or digits i.e no other chars
    	if (re_acc_postbank.test(accNum)) {
	        return true;
	    } else {
	    	return false
	    }
	}
	
    // If it's not Postbank than it should be regular bank account number.
    if (!re_acc_bank.test(accNum)) {
        return false;
    }	

    accNum = accNum.replace(/\./g, '');
    // prepare for eleven-proof be adding a leading zero if length <10.
    accNum = '0' + accNum;
    accNum = accNum.substring(accNum.length - 10);

    var productSum = 0;
    for (var i = 0; i < accNum.length; i++) {
        productSum += (accNum.length - i) * parseInt(accNum.charAt(i));
    }

    // The product dived by 11 should give a whole number.
    if (productSum % 11 != 0) {
        return false;
    }
    return true;
};


Validator.MIN_LENGTH = function (v, l) {
    if (v != null && v.toString && v.toString().length < l) {
        return false;
    }
    return true;
}
Validator.MAX_LENGTH = function (v, l) {
    if (v != null && v.toString && v.toString().length > l) {
        return false;
    }
    return true;
}
Validator.MIN_VALUE = function (v, m) {
    if (v != null && v < m) {
        return false;
    }
    return true;
}
Validator.MAX_VALUE = function (v, m) {
    if (v != null && v > m) {
        return false;
    }
    return true;
}

Validator.replace = function (m, o) {
    m = m + '';
    for (var p in o) {
   		var v = o[p];    
        m = m.replace('{%'+p + '}', v);
    }
    return m;    
}

Validator.prototype = {
    options: {
        handleError: function (error) {
            if (error == error.validator.errors.first()) {
                // first rule
                if (!error.message && error.rule && error.rule.message) {
                    error.message = error.rule.message;
                }
                alert(Validator.replace(error.message, error));
                field = $A($(error.field)).first();
                if (field.focus) {
                    field.focus();
                } else if (field.select) {
                    field.select();
                }
            }
        }
    },
    form: null,
    errors: [],
    initialize: function(form, options) {
        this.form = form;
        this.options = Object.extend(this.options, options || {});
    },
    check: function (obj) {
        obj.validator = this;
        obj.field = $FE(this.form, obj.field);
        if (obj.field) {
            if (obj.value == null) {
                obj.value = this.getValue(obj.field);
            }
            //TODO: check if neccesary property are there
            var me = this;
            $A(obj.value).each(
                function (value) {
                    if ((obj.rule.test && !obj.rule.test(value)) || (!obj.rule.test && !obj.rule(value)) ) {
                        me.errors.push(obj);
                    }
                }
            );
        }  
    },
    isError: function () {
        return (this.errors.length > 0);
    },
    reset: function () {
        this.errors = [];
    },
    handleErrors: function () {
        if (this.isError()) {
            this.errors.each(this.options.handleError);
            this.errors = [];
            return true;
        }
        this.errors = [];
        return false;
    },
    getValue: function (elements) {
        var value = [];
        var form = this.form;
        if (elements.constructor != Array) {
            elements = [elements];
        }
        
        elements.each(
            function (element) {
                switch (element.type) {
                    case 'checkbox' :
                    case 'radio' :
                        if (element.checked) {
                            value.push(element.value);
                        }
                        break;
                    case 'select-one' :
                        var index = element.selectedIndex;
    
                        if (index >= 0) {
                            var opt = element.options[index];
                            value.push(opt.value);
                        }
                        break;
                    case 'select-multiple' :
                        element.option.each(
                            function (option) {
                                if (option.selected) {
                                    value.push(option.value);
                                }
                            }
                        );
                        break;
                    case 'textarea' :
                    case 'text' :
                    case 'file' :
                    case 'hidden' :
                    case 'password' :
                        value.push(element.value);
                }            
            }
        );
        return value.length > 1 ? value : value[0] || '';
    }    
}

function dumpArray(a) {
    if (a.constructor == Array) {
        return a.join(', ');
    } else {
        return a
    }
}

$FE = function (form) {
    elements=[];
    for (var i = 1; i < arguments.length; i++) {
        element = arguments[i];
        if (element.constructor == Array) {
            elements = elements.concat($FE.apply(this, [form].concat(element)));
        } else if (typeof element == 'string') {
            element = form[element];
            if (element) {
                elements = elements.concat($FE.apply(this, [form].concat(element)));
            }
        } else if (element.length && !element.form) {
            // can also be a nodelist
            result = []
            for (var j = 0; j < element.length; j++) {
                result.push(element[j]);
            }
            elements = elements.concat($FE.apply(this, [form].concat(result)));
        } else if (element.form && element.form == form) {
            elements.push(element);
        } else {
            //nothing
        }
    }
    return elements.reduce();    
}