var MSG_WRONG_VALUE;
var MSG_LINKED_FIELDS;
var MSG_MANDATORY_FIELD;

var STATE_UNDEFINED = -1; //Marque un champ en cours de vérification
var STATE_VALID = 1; //Marque un champ rempli avec une valeur correcte
var STATE_INVALID = 0; //Marque un champ non rempli ou rempli avec une valeur erronée

/*
* Renvoie le nom du champ
*/
function formGenerique_getFieldName(field){
	if(field.length){
		if(field[0].tagName.toLowerCase() == "option"){
			return field[0].parentNode.name;
		}
		else{
			return field[0].name;
		}
	}
	else{
		return field.name;
	}
}

/*
* Vérifie si un champ est obligatoire en récupérant l'attribut mandatory
*/
function formGenerique_isMandatory(field){
	if(field.length != undefined && field.length > 0){
		if(field[0].tagName.toLowerCase() == "option"){
			field = field[0].parentNode;
		}
		else{
			field = field[0];
		}
	}
	var attr = field.attributes.getNamedItem("mandatory");
	return attr && attr.value.toLowerCase() == "true";
}

/*
*
*/
function formGenerique_getAttribute(field, attrName){
	if(field.length != undefined && field.length > 0){
		if(field[0].tagName.toLowerCase() == "option"){
			field = field[0].parentNode;
		}
		else{
			field = field[0];
		}
	}
	var attr = field.attributes.getNamedItem(attrName);
	return (attr) ? attr.value : null;
}

/*
* Objet pour remonter une erreur
*/
function ErrorObject(field, msg){
	this.Field = field;
	this.Message = msg;
}

/*
* Renvoie le type de champ en récupérant l'attribut subtype ou à défaut type
*/
function formGenerique_getType(field){
	if(field.length != undefined && field.length > 0) field = field[0];

	var fldType = field.attributes.getNamedItem("subtype");
	if(fldType == undefined){
		fldType = field.attributes.getNamedItem("type");
	}

	fldType = (fldType) ? fldType.value : "text";
	fldType = (fldType == "text" && field.tagName.toLowerCase() != "input") ? field.tagName : fldType;

	fldType = (fldType.indexOf("date") == 0) ? "date" : fldType;

	return fldType.toLowerCase();
}

/*
* Vérifie que le champ contient une valeur
*/
function formGenerique_isFieldPopulated(field){
	var fldType = formGenerique_getType(field);

	switch(fldType){
		case "radio":
			return checkRadioButtonChecked(field);
			
		case "checkbox":
			return checkCheckBoxChecked(field);
			
		case "option":
			return checkSelectBoxSelected(field);
			
		case "rib_rip":
		case "text":
		case "attachment":
		case "decimal":
		case "integer":
		case "phone_number":
		case "e_mail":
		case "zip_code":
		case "ca_number":
		case "textarea":
		case "date":
		case "siren_siret":
			return field.value != "";
		
		default:
			throw "Type inconnu : " + fldType;
	}
}

function formGenerique_testBounds(field){
	var fldType = formGenerique_getType(field);
	
	var min = formGenerique_getAttribute(field, "min");
	var max = formGenerique_getAttribute(field, "max");

	switch(fldType){
		case "date":
			var format = formGenerique_getAttribute(field, "format");
			var date = formGenerique_splitDate(field.value, format);

			min = (min) ? formGenerique_splitDate(min) : [ 1,  1,-9999];
			max = (max) ? formGenerique_splitDate(max) : [31, 12, 9999];

			var gtMin = date[2] > min[2];
			if(!gtMin && date[2] == min[2]){
				gtMin = date[1] > min[1];
				if(!gtMin && date[1] == min[1]){
					gtMin = date[0] >= min[0];
				}
			}
			
			var ltMax = date[2] < max[2];
			if(!ltMax && date[2] == max[2]){
				ltMax = date[1] < max[1];
				if(!ltMax && date[1] == max[1]){
					ltMax = date[0] <= max[0];
				}
			}
			
			return gtMin && ltMax

		case "integer":
		case "decimal":
			min = (min) ? new Number(min) : new Number(Number.NEGATIVE_INFINITY);
			max = (max) ? new Number(max) : new Number(Number.POSITIVE_INFINITY);
			var val = new Number(field.value.replace(/,/g, "."));
			return val >= min && val <= max;
	}
}

function formGenerique_splitDate(val, format){
	var mmAAAA = (format == "MM/AAAA");

	var pattern = "^(\\d{1,2})";
	pattern += mmAAAA ? "" : "/(\\d{1,2})";
	pattern += "/(\\d{4,4})$";
	
	var rx = new RegExp(pattern);

	var matches = rx.exec(val);
	
	var day = mmAAAA ? "1" : matches[1];
	var idxM = mmAAAA ? 1 : 2;
	var idxY = mmAAAA ? 2 : 3;

	return [day*1, matches[idxM]*1, matches[idxY]*1];
}

/*
* Vérifie la validité d'une valeur en fonction du type du champ
*/
function formGenerique_checkFieldValue(field){
	var fldType = formGenerique_getType(field);

	switch(fldType){
		case "attachment":
		case "radio":
		case "checkbox":
		case "option":
			return true;
		case "text":
			var regexp = formGenerique_getAttribute(field, "regexp");
			if(regexp){
				var rx = eval(regexp);

				return rx.test(field.value);
			}
			else{
				return true;
			}
		case "textarea":
			var maxLength = formGenerique_getAttribute(field, "maxlength");
			maxLength = (maxLength == null) ? Number.POSITIVE_INFINITY : maxLength;
			return field.value.length <= maxLength;
		case "phone_number":
			return isEntierValid(field.value) && isComplet10Valid(field.value);
			
		case "e_mail":
			return checkEmailFormat(field.value);
			
		case "zip_code":
			return checkPostalCodeFormat(field.value);
			
		case "date":
			return checkDateFormat(field.value, formGenerique_getAttribute(field, "format")) && formGenerique_testBounds(field);
			
		case "integer":
			return isInteger(field.value) && formGenerique_testBounds(field);
			
		case "decimal":
			return isDecimal(field.value) && formGenerique_testBounds(field);
			
		case "ca_number":
			return isEntierValid(field.value) && isComplet11Valid(field.value);

		case "rib_rip":
			return checkRibRipFormat(field.value);

		case "siren_siret":
			return checkSirenSiretFormat(field.value);
		
		default:
			throw "Type inconnu : " + fldType;
	}
}

/*
* Vérifie la validité d'un champ. Tient compte des éléments suivants :
* - champ obligatoire ou non
* - valeur saisie correcte ou non
* - saisie correcte d'au moins un des champs liés. (attribut linkedFields, contenant une liste de noms de champs séparés par un '|')
*/
function formGenerique_checkField(_form, field){
	//Ajoute ce champ à la liste des champs visités avec un état "indéfini" marquant qu'il est en cours de validation
	formGenerique_fieldsDone.put(formGenerique_getFieldName(field), STATE_UNDEFINED);

	var libFront = formGenerique_getAttribute(field, "libFront").toUpperCase();
	
	mandatory = formGenerique_isMandatory(field);
	
	var error = null;

	var pop = formGenerique_isFieldPopulated(field);

	if(!pop){
		//L'état est mis à jour à invalide car vide
		formGenerique_fieldsDone.put(formGenerique_getFieldName(field), STATE_INVALID);
		//On remonte une erreur si le champ est obligatoire
		if(mandatory){
			error = new ErrorObject(field, MSG_MANDATORY_FIELD + libFront);
		}
	}
	else{
		var valOk = formGenerique_checkFieldValue(field);
		if(!valOk){
			//L'état est mis à jour à invalide car contenant une valeur incorrecte
			formGenerique_fieldsDone.put(formGenerique_getFieldName(field), STATE_INVALID);
			var errMsg = formGenerique_getAttribute(field, "errMsg");
			errMsg = (errMsg) ? errMsg : MSG_WRONG_VALUE + libFront + "."
			error = new ErrorObject(field, errMsg);
		}
		else{
			//L'état est mis à jour à valide
			formGenerique_fieldsDone.put(formGenerique_getFieldName(field), STATE_VALID);
			error = formGenerique_checkLinkedFields(_form, field);
		}
	}
	
	return error;
}

/*
* Vérifie la validité des champs liés à ce champ. Le processus est récursif.
*/
function formGenerique_checkLinkedFields(_form, field){
	var fieldColl = [];
	if(field.length != undefined && field.length > 0){
		for(var k=0; k<field.length; k++){
			if(field[k].checked || field[k].selected){
				fieldColl.push(field[k]);
			}
		}
	}
	else{
		fieldColl.push(field);
	}
	
	for(var fld=0; fld<fieldColl.length; fld++){
		field = fieldColl[fld];

		var error = null;
		var linkedFields = formGenerique_getAttribute(field, "linkedFields");

		if(linkedFields){
			//Récupération de la liste des champs liés
			var names = linkedFields.split("|");

			var ok = false;

			//On parcours les champs liés jusqu'à en trouver un non vide et valide
			for(var k=0; k<names.length; k++){
				var fieldOK = formGenerique_fieldsDone.get(names[k]);

				//Si on a déjà vu ce champ
				if(fieldOK){
					//Si l'état du champ est indéfini, cela signifie qu'il est en cours 
					//de validation, donc qu'il y a un circuit. Exemple : A -> B -> *A*
					//Donc A est valide car on n'aurait pas pu passer de A -> B dans l'étape précédente.
					if(fieldOK == STATE_UNDEFINED || fieldOK == STATE_VALID){
						ok = true;
						break;
					}
					else if(fieldOK == STATE_INVALID){//On teste le suivant
						continue;
					}
				}
				else{
					formGenerique_fieldsDone.put(names[k], STATE_UNDEFINED);

					field = _form.elements[names[k]];

					var libFront = formGenerique_getAttribute(field, "libFront").toUpperCase();
					pop = formGenerique_isFieldPopulated(field);

					if(!pop){
						formGenerique_fieldsDone.put(names[k], STATE_INVALID);
						continue;
					}
					else{
						var valOk = formGenerique_checkFieldValue(field);

						if(!valOk){
							formGenerique_fieldsDone.put(names[k], STATE_INVALID);
							var errMsg = formGenerique_getAttribute(field, "errMsg");
							errMsg = (errMsg) ? errMsg : MSG_WRONG_VALUE + libFront + ".";
							error = new ErrorObject(field, errMsg);
							break;
						}
						else{
							error = formGenerique_checkLinkedFields(_form, field);
							if(!error){
								formGenerique_fieldsDone.put(names[k], STATE_VALID);
							}
							ok = true;
							break;
						}
					}
				}
			}

			//Si aucun champ lié n'est correct et aucune erreur plus profonde n'est remontée, 
			//on en crée une qui sera passée à l'appelant.
			if(!ok && !error){
				var msgErr;

				msgErr  = MSG_LINKED_FIELDS + formGenerique_getAttribute(_form.elements[names[0]], "libFront").toUpperCase();
				for(var k=1; k<names.length; k++){
					var libFront = formGenerique_getAttribute(_form.elements[names[k]], "libFront").toUpperCase();
					msgErr += ", " + libFront;
				}


				error = new ErrorObject(_form.elements[names[0]], msgErr);
				break;
			}
		}
	}
	
	return error;
}

/*
* Map globale gardant la trace des champs liés déjà visités.
*/
var formGenerique_fieldsDone;

/*
* Vérifie les champs par ordre de l'attribut tabIndex qui doit être spécifié explicitement.
*/
function formGenerique_checkFormFields(_form){
	var fields = new Array();

	formGenerique_fieldsDone = new Map();

	for(var i=0; i<_form.elements.length; i++){
		var type = _form.elements[i].attributes.getNamedItem("type");
		var subtype = _form.elements[i].attributes.getNamedItem("subtype");

		if(
			!fields.contains(_form.elements[i].name)
			&& (
				!type 
				|| (type.value.toLowerCase() != "button" && !(type.value.toLowerCase() == "hidden" && !subtype))
			)
		){
			fields.push(_form.elements[i].name);
		}
	}

	fields.sort(
		function(name1, name2){
			var elm1 = document.getElementsByName(name1)[0];
			var elm2 = document.getElementsByName(name2)[0];

			var tabindex1 = elm1.attributes.getNamedItem("tabindex");
			tabindex1 = (tabindex1) ? Number(tabindex1.value) : 0;
			
			var tabindex2 = elm2.attributes.getNamedItem("tabindex");
			tabindex2 = (tabindex2) ? Number(tabindex2.value) : 0;

			return tabindex1 - tabindex2; 
		}
	);
	var ret = true;
	
	for(var i=0; i<fields.length; i++){
		var field = _form.elements[fields[i]];
		
		var tabIndex;
		
		if(field.length){
			if(field[0].tagName.toLowerCase() == "option"){
				tabIndex = field[0].parentNode.attributes.getNamedItem("tabindex").value;
			}
			else{
				tabIndex = field[0].attributes.getNamedItem("tabindex").value;
			}
		}
		else{
			tabIndex = field.attributes.getNamedItem("tabindex").value;
		}

		var error = formGenerique_checkField(_form, field);
		if(error){
			alert(error.Message);
			var fld = (error.Field.length) ? error.Field[0] : error.Field;
			if(fld.focus != undefined){
				fld.focus();
			}
			return false;
		}
	}
	
	return true;
}

function formGenerique_send(_form) {
	if (formGenerique_checkFormFields(_form)) {
		if(_form.AutorisationOptin && _form.case_optin){
			_form.AutorisationOptin.value = checkCheckBoxChecked(_form.case_optin) ? "oui" : "non";
		}
		_form.submit();
	}
}

function updateFileValue(id, val){
	var elmt = document.getElementById(id);
	elmt.value = val;
}