ENS.FormValidator = function(form, validators, debug){
	var _this = this;
	_this.version    = "1.0";
	_this.name       = "FormValidator";
	_this.src        = "formvalidator.js";
	_this.gscope     = false;
	_this.form       = ENS.$(form); // reference to form
	_this.elements   = []; // list of id's in the form
	_this.validation_elements = []; // list of id's to validate
	_this.validator_stacks    = {}; // map of id/array of validators pairs
	
	_this.Validators = validators || {}; // 
	_this.debug = debug || false;
	
	// a validator object for a given element
	var ElementValidator = function(element){ 
		var _this = this;
		_this.element = ENS.$(element);
		_this.validators = {};
		_this.validatorkeys = []; 
		
		_this.setValidator = function(name, func) {
			/*
			 *  @name : the name used to access the validator
			 *  @func : a javascript function which takes a value parameter and an originating element and returns a boolean (the result of validating the value)
						example: 
							* a function which checks if the element's value is the letter: "a" would look like:
							function myValidator(value, element) {return value=='a';}
							* to add the validator use: setValidator("check_value_is_a", myValidator);
			 */
			_this.validators[name] = func;
			if(_this.validatorkeys.indexOf(name)==-1){
				_this.validatorkeys.push(name);
			}
		};
	
		_this.validate = function(name) {
			/*
			 *  @name : the name of the validator to use
			 */
			return (!(_this.validators[name]) ? false : _this.validators[name](_this.getValue(), _this.element));
		};
	
		_this.getValue = function(){
			var value = _this.element.value || "";
			return value;
		};
	};
	
	/* called on submit of form */
	_this.validate = function(e){
		_this.resetErrors();
		var errors = _this.getValidationErrors();
		var errResult = _this.processErrors(errors);
		if(errResult.hasErrors){
			ENS.cancelEvent(e);
			alert('Het formulier bevat fouten. Controleer uw invoer.');
			if(!errResult.feedback && ENS.debug){
				alert(_this.errorsToString(errors));
				// if(_this.debug) alert(_this.errorsToString(errors));
                // alert('Het fortmulier bevat fouten. Controleer uw invoer.');
			}
			return false;
		}
		return true;
	};
	
	/* hide all error feedback */
	_this.resetErrors = function(){
		for(var i=0,el;i<_this.validation_elements.length,el=_this.validation_elements[i];i++){
			var se = ENS.$(el+"_syntax_error"); // syntax error element
			var rq = ENS.$(el+"_required_error"); // required error element
			if(se) ENS.addClass(se,"hidden");
			if(rq) ENS.addClass(rq,"hidden")
		}
	};
	
	/* show the error-feedback elements */
	_this.processErrors = function(errorStruct){
		var processResult = new Object();
		processResult.hasErrors = false;
		processResult.feedback = true;
		for(var element in errorStruct){
			processResult.hasErrors = true;
			for(var errtype in errorStruct[element]){
				switch(errorStruct[element][errtype]){
					case "required": 
						var rq = ENS.$(element+"_required_error"); // required error element
						if(rq) {
							ENS.removeClass(rq,"hidden");
						}
						else {
							processResult.feedback = false;
						}
						break;
					default:
						var se = ENS.$(element+"_syntax_error"); // required error element
						if(se){
							ENS.removeClass(se,"hidden");
						}
						else {
							processResult.feedback = false;
						}
				}
			}
		}
		return processResult;
	};
	
	/* take errorstruct and convert it to string */
	_this.errorsToString = function(errStruct){
		var errString = "";
		for(var err in errStruct){
			if(errString=="") errString = "Er zijn foutmeldingen opgetreden voor de volgende velden:";
			errString += "\n";
			errString += "- " + err + " : " + errStruct[err];
		}
		return errString;
	};
	
	/* return a struct of element_id's and error_types if validation errors occur */
	_this.getValidationErrors = function(){
		var els = _this.getValidationElements();
		var ok = true;
		var errors = {};
		for(var i=0,el;i<els.length,el=els[i];i++){
			var validation_element = _this.validator_stacks[el];
			if(validation_element.validatorkeys.length){
				var vals = validation_element.validators;
				for(var v in vals){
					if(typeof(vals[v])=="function"){
						ok = validation_element.validate(v);
						if(!ok){
							var er = errors[el] || [];
							er.push(v);
							errors[el] = er;
						}
						//alert("validationtype: " + v + "\nfor element " + validation_element.element + "\nwhich has value: " + validation_element.getValue() + "\nreturns: " + ok);
					}
				}
			}
		}
		return errors;
	};
	
	/* create a new validator object */
	_this.newValidator = function(element){
		return new ElementValidator(element);
	};
	
	/* walk form elements, 
		create and store validator object for the given element in a map, 
		add validation functions based on classnames 
	*/
	_this.setElements = function(){
		for(var i=0;i<_this.form.elements.length;i++){
			if(_this.inspectable(_this.form.elements[i])){
				_this.elements.push(_this.getElementId(_this.form.elements[i]));
				_this.validator_stacks[_this.elements[_this.elements.length-1]] = _this.newValidator(_this.form.elements[i]); // stuff in validator functions later
				_this.setValidatorsFromClass(_this.validator_stacks[_this.elements[_this.elements.length-1]], _this.form.elements[i]);
			}
		}
	};
    
	/* set a unique form_id if it does not exist */    
	_this.getElementId = function(element){
		var id = element.id || "";
		if(id=="") {
			element.id = "__FORMVALIDATOR__" + _this.elements.length;
		}
		return element.id;
	};
    
	/* check if given element is a form element */
     _this.inspectable = function(element){
		return (ENS.$(element) && (["input","select","textarea","button"].indexOf(ENS.$(element).tagName.toLowerCase())!=-1)) ? true : false;
	};

	/* return all elements in the form */    
	_this.getElements = function(){
		return _this.elements;
	};
	
	/* return elements that are validated */    
	_this.getValidationElements = function(){
		return _this.validation_elements;
	};
    
	/* return current validators */    
    _this.getElementsStack = function(){
		return _this.validator_stacks;
	};
	
	/* push (form)element to validate in the validation_elements array */
	_this.addValidationElement = function(element_id){
		if(_this.validation_elements.indexOf(element_id)==-1){
			_this.validation_elements.push(element_id);
		}
	};
	
	/* add validation functions to the validator based on the element's classname(s) */
	_this.setValidatorsFromClass = function(validator, element){
		var classes = element.className.split(" ");
		for(var c=0;c<classes.length;c++){
			if(_this.Validators[classes[c]]){
				validator.setValidator(classes[c], _this.Validators[classes[c]]);
				_this.addValidationElement(element.id);
			}
		}
		return validator;
	};
	
	// walk form elements and add the validation hooks
	_this.setElements();
};


/* after onload, walk all forms and hook the validator to the form's submit */
ENS.initFormValidators = function(){
	var fv = new Array();
	for(var i=0;i<document.forms.length;i++){
		fv.push(new ENS.FormValidator(document.forms[i], ENS.Validators));
		ENS.knownValidators[document.forms[i].id] = fv[fv.length-1];
		ENS.addEvent(document.forms[i], "submit", fv[i].validate);
	}
	// console.debug(fv);
};

ENS.knownValidators = {};

// attach the validators after onload
ENS.addEvent(window, "load", ENS.initFormValidators);


