window['App'] = window.App || {};
App.Forms = {

	FormList : {},
	
	/**
	 * Valide les règles associées à un champ
	 * @param DOMElement Input, Select ou Textarea. Les autres sont ignorés
	 * @param Event event Optionel. A utiliser pour définir si le champ doit être controlé. 
	 * @return boolean True si toutes les règles sont validées
	 */
	checkFieldValidity: function(_field, _event) {
		try {
			// Si le champ est envoyé par un évènement
			var event, field;
			if (_event) {
				event = _event;
			} else {
				event = _field['target'] ? _field : null;
			}
			field = _field['target'] ? _field['target'] : _field;
			
			// On n'applique que sur les champs d'entrées
			if (field.nodeName !== 'INPUT'  &&
				field.nodeName !== 'SELECT' &&
				field.nodeName !== 'TEXTAREA') return true;
			
			// On ignore la validation de champs désactivés
			if (field.disabled) return undefined;
			
			// On recherche le nom du formulaire de validation
			var jField = jQuery(field),
				jSubForm = jField.parents('fieldset.jqSubForm').get(0);
			
			var form = jSubForm || field.form,
				formName = form['id'] ? form.id.replace(/:.*$/, '') : 'form',
				isMultiValue = (/\[\]$/.exec(field.name) !== null),
				fieldName = jSubForm ? String(field.name).replace(/^.*\[([^\]]+)\]$/, '$1') : String(field.name).replace(/\[\]$/, '');
			
			// Aucun control disponible pour le formulaire, le champ est valide
			if (!App.Forms.FormList[formName]) return true;
			var ruleset = App.Forms.FormList[formName][fieldName];
			// Pas de validateur ? Le champ est valide
			if (!ruleset) return true;
			ruleset.options = ruleset['options'] || {};
			
			// si le controle est fait par un évènement, on controle que l'on puisse bien le passer
			if (event && ruleset['checkOn']) {
				var doCheck = ruleset.checkOn[event.type] === undefined ? true : ruleset.checkOn[event.type];
				if (!doCheck) return;
			}
			
			// Cas spécifique pour FCKEditor, on synchronise le champ d'origine avant de lui appliquer les controles
			var isFCKEditor = jField.hasClass('jqFCKEditor')
			if (isFCKEditor) {
				var oEditor = FCKeditorAPI.GetInstance(field.id);
				//if (oEditor.ResetIsDirty())
				oEditor.UpdateLinkedField();
				var fckTextareaContent = field.value;
				// On dégage les balises pour les contrôles
				field.value = Utile.htmlEntitiesDecode(fckTextareaContent.replace(/(<\/?[\S][^>]*>)/gi, ""));
			}
			
			// Suppression de la valeur de tooltip si existante
			if (ruleset['tooltip'] && field.value == ruleset['tooltip']) {
				field.value = '';
			}
			
			// Filtrage
			var filteredValue = App.Forms.applyFilters(field.value, ruleset);
			
			// Validation des règles associées
			var canBeEmpty = !App.Forms.rulesetHasValidator(ruleset, "NotEmpty");
			
			if (!canBeEmpty || filteredValue != "") {
				var i = 0;
				while (ruleset[i]) {
					if (ruleset[i].disabled) { i++; continue; }
					var validatorName = ruleset[i].name,
						originalValue = field.value;
					field.value = filteredValue;
					var validatorReturn = App.Forms.Validator[validatorName](field, ruleset[i].parameters);
					field.value = originalValue;
					if (validatorReturn !== true) {
						if (isFCKEditor) field.value = fckTextareaContent;
						return App.Forms.displayFieldStatus(field, false, validatorReturn, ruleset, i);
					}
					i++;
				}
			}
			
			// Si toutes les règles sont validées
			if (isFCKEditor) field.value = fckTextareaContent;
			return App.Forms.displayFieldStatus(field, true, null, ruleset, i - 1);
		}
		catch (e) {
			alert(e);
		}
	},
	
	applyFilters: function(_value, ruleset) {
		try {
			var value= _value;
			if (ruleset['filters'] && ruleset.filters.length) {
				var i = 0;
				while (ruleset.filters[i]) {
					var filterName = ruleset.filters[i].name;
					value = App.Forms.Filter[filterName](value, ruleset.filters[i].parameters);
					i++;
				}				
			}
			return value;
		}
		catch (e) {
			alert(e);
		}
	},
	
	/**
	 * Valide un formulaire
	 * @param DOMElement form Le noeud formulaire a valider
	 */
	checkFormValidity: function(form) {
		var event = (form['target']) ? form : null;
		// Si le formulaire est envoyé par un évènement
		form = form['target'] || form;
		if (form.nodeName != 'FORM') return;
		
		var formName = form['id'] ? form.id : 'form',
			formValidity  = true,
			fieldValidity = false,
			validatedFields = {}; // Contient la liste des champs déjà validés (les noms de champs)
		
		if (App.Forms.FormList[formName + '_preValidation']) {
			if (!App.Forms.FormList[formName + '_preValidation']())
			return false;
		}
		
		// Aucun control disponible pour le formulaire, il est valide
		if (!App.Forms.FormList[formName]) {
			return true;
		}
		
		var fieldToFocus = null;
		for (var i = 0; i < form.elements.length; i++) {
			var field = form.elements[i];
			if (field['name'] === undefined) continue;
			var fieldId = field.name.replace(/\[\]/, '');
			// Nom de champs déjà validé ? On passe au suivant
			if (validatedFields[fieldId]) continue;
			
			fieldValidity  = App.Forms.checkFieldValidity(form.elements[i], event);
			// Vérification du champ ignoré (Champ désactivé ou autre)
			if (fieldValidity === undefined) continue;
			formValidity  &= fieldValidity;
			if (!fieldToFocus && !fieldValidity)
				fieldToFocus = field;
			validatedFields[fieldId] = true;
		}
		
		// On recherche les champs vides non validés (Cas des listes dynamiques)
		for (var id in App.Forms.FormList[formName]) {
			var ruleset = App.Forms.FormList[formName][id];
			var i = 0;
			while (ruleset[i]) {
				if (ruleset[i].control == "NotEmpty") {
					if (validatedFields[id] === undefined) {
						var errorMessage = ""
						if (ruleset[i]['errorMessage']) errorMessage = ruleset[i]['errorMessage'];
						fieldValidity  = App.Forms.displayFieldStatus(id, false, errorMessage, ruleset, i);
						formValidity &= fieldValidity;
					}
				}
				i++;
			}
		}
		
		if (event && !formValidity)
			event.stopPropagation();
		
		if (!formValidity) {
			if (fieldToFocus) {
				// Sous IE, un champ hidden ou non affiché provoque une erreur ici.
				try { fieldToFocus.focus(); }  catch (e) { }
			}
			if (jQuery['scrollTo'])
				jQuery['scrollTo'](fieldToFocus, 250)
			else
				window.scrollTo(0, 200);
		}

		formValidity = formValidity ? true : false;
		
		if (App.Forms.FormList[formName + '_postValidation']) {
			if (!App.Forms.FormList[formName + '_postValidation'](formValidity))
				return false;
		}
		
		return formValidity; // On s'assure qu'on renvoi bien un booleen
	},
	
	/**
	 * Vide le status d'erreur du champ
	 */
	clearFieldStatus: function(field) {
		var fieldName = String(field.name).replace('[]', ''),
			errorDiv = document.getElementById('errors_'+fieldName);
		if (errorDiv) jQuery(errorDiv).html('').removeClass('isValid').removeClass('isInvalid');
	},
	
	/**
	 * Affiche le status d'un champ
	 * @param DOMElement|string field Le champs pour lequel on affiche le status. Un noeud DOM ou l'id d'un champ
	 * @param boolean isValid True si le champ est valide
	 * @param string message Le massage a afficher
	 * @param object rulset Le jeu de controle du champ
	 * @param int rulesetIndex L'index dans le jeu de controle a utiliser pour le parsing du message d'erreur
	 * @return boolean Renvoi isValid
	 */
	displayFieldStatus: function(field, isValid, message, ruleset, rulesetIndex) {
		if (!ruleset) ruleset = {};
		ruleset.message = ruleset['message'] || {};
		if (ruleset['canDisplayValidMessage'] === undefined)
			ruleset['canDisplayValidMessage'] = true;
		
		// Si on trouve le champ on le prend, sinon on garde la chaine de caractère pour trouver la zone de notification
		if (typeof(field) == 'string') {
			var newField = document.getElementById(field);
			if (newField) field = newField;
		}
		
		if (typeof(field) == 'object') {
			var form = field.form,
				formName = form['name'] ? form.name : 'form',
				isMultiValue = field.name.match(/\[\]$/),
				fieldName = String(field.name).replace('[]', ''),
				jField = jQuery(field),
				showValidMessage = ruleset.message['showValidMessage'] === undefined ? true : ruleset.message['showValidMessage'];
			if (fieldName != '') {
				var errorDiv = document.getElementById('errors_'+fieldName);
			} else {
				var errorDiv = document.getElementById('errors_'+field.id);
			}
		} else if (typeof(field) == 'string') {
			var fieldName = field,
				errorDiv = document.getElementById('errors_'+fieldName),
				showValidMessage = true;
		}
		
		if (!errorDiv) {
			errorDiv = document.createElement('DIV');
			errorDiv.className = "errors";
			if (fieldName != '') {
				errorDiv.id = "errors_" + fieldName;
			} else {
				errorDiv.id = "errors_" + field.id;
			}
			if (jField) jField.after(errorDiv);
			else document.body.appendChild(errorDiv); // Pas trouvé ? On ajoute à la fin du document
		}
		
		if ((!isValid) || (showValidMessage && isValid)) {
			errorDiv.style.display = '';
			var jErrorDiv = jQuery(errorDiv);
			// Pour faciliter l'intégration
			if (isValid) {
				jErrorDiv.removeClass('isInvalid').addClass('isValid');
				if (jField) jField.removeClass('isInvalid').addClass('isValid');
			}
			else {
				jErrorDiv.removeClass('isValid').addClass('isInvalid');
				if (jField) jField.removeClass('isValid').addClass('isInvalid');
			}
		}
		else {
			errorDiv.style.display = 'none';
		}
		try {
			if (message && ruleset[rulesetIndex] && ruleset[rulesetIndex]['errorMessage']) {
				message = ruleset[rulesetIndex]['errorMessage'];
			} else {
				message = message ? ruleset.message[message] || translator[message] || message : null;
			}
		} catch (e) {}
		
		// Parse du message
		if (message && rulesetIndex !== undefined && ruleset[rulesetIndex] && ruleset[rulesetIndex]['parameters']) {
			var parameters = ruleset[rulesetIndex]['parameters'];
			for (var i in parameters) message = message.replace('%'+i+'%', parameters[i]);
		}
		
		// On affiche ou pas la validité du message
		if (isValid && !ruleset.canDisplayValidMessage) {
			errorDiv.innerHTML = '';
			return isValid;
		}
		
		errorDiv.innerHTML = (!isValid && message) ? message : '<img src="images/valid.gif" alt="'+App.trad("Champs valide")+'" border="0">';
		
		return isValid;
	},
	
	rulesetHasValidator: function (ruleset, validator) {
		var i = 0, canBeEmpty = true;
		while (ruleset[i]) {
			if (!ruleset[i].disabled && (ruleset[i].control == validator || ruleset[i].control == name)) {
				return true;
			}
			i++;
		}
		return false;
	},
	
	disableRulesetValidator: function (ruleset, validator, isDisabled) {
		var i = 0, canBeEmpty = true;
		while (ruleset[i]) {
			if (ruleset[i].control == validator || ruleset[i].control == name) {
				ruleset[i].disabled = isDisabled;
				return true;
			}
			i++;
		}
		return false;
	},
	
	Validator : {
		Zend_Validate_NotEmpty: function(element, parameters) {
			if (element.type == 'checkbox' || element.type == 'radio')
				return	(jQuery("input:checked[type="+element.type+"][name='"+element.name+"']").length > 0) ? true : 'notChecked';
			else
				return 	(element.value.replace("\r", "").replace("\n", " ").match(/^\s*$/) === null)	? true : 'isEmpty';
		},
		
		Zend_Validate_Alnum: function(element, parameters)	{
			var value = element.value;
			if (parameters.allowWhiteSpace && value.match(/^[a-z0-9\s]*$/i) !== null)
				return true;
			if (value.match(/^[a-z0-9\s]*$/i) !== null)
				return true;
			return 'notAlnum';
		},
		
		Zend_Validate_Float: function(element, parameters)	{
			var value = String(element.value);
			value = jQuery.trim(value.replace(',', '.')); // Petit hack pour être sur de toujours avoir un flottant avec point
			element.value = value;
			if (value == '') return true;
			if (value.match(/^[-+]?\d+(?:[\.]\d+)?$/) === null) return 'notFloat';
			if (parseFloat(value) !== NaN) return true;
			return 'notFloat';
		},
		
		Zend_Validate_Int: function(element, parameters)	{
			var value = String(element.value);
			if (value == '') return true;
			if (value.match(/^[-+]?\d+$/) === null) return 'notInt';
			if (parseInt(value) !== NaN) return true;
			return 'notInt';
		},
		
		ZendX_Validate_IntOrEmpty: function(element, parameters)	{
			if (App.Forms.Validator.Zend_Validate_NotEmpty(element, parameters) === true)
				return App.Forms.Validator.Zend_Validate_Int(element, parameters);
			return true;
		},
		
		Zend_Validate_Between: function(element, parameters) {
			if (element.value == '') return true;
			var value = parseFloat(element.value);
			if (value === NaN) return 'notBetween';
			if (value >= parameters.min && value <= parameters.max) return true; 
			return 'notBetween';
		},
		
		ZendX_Validate_Confirm: function(element, parameters) {
			var confirmElement = document.getElementById(parameters.confirmFieldId);
			if (!confirmElement) return;
			if (element.value == confirmElement.value)
				return true;
			return 'notSameValue';
		},
		
		Zend_Validate_InArray: function(element, parameters) {
			if (typeof(parameters.haystack) == 'string') {
				parameters.haystack = String(parameters.haystack).split(";");
			}
			if (parameters.haystack instanceof Array) {
				/* IE */
				if (!parameters.haystack.indexOf) {
					for (var i = 0; i < parameters.haystack.length; i++) {
						if (parameters.haystack[i]==element.value || parameters.haystack[i]==parseInt(element.value)) {
							return(true);
						}
					}
				/* Firefox */
				} else if (parameters.haystack.indexOf(element.value) >= 0 || parameters.haystack.indexOf(parseInt(element.value)) >= 0) {
					return true;
				}
			} else if (parameters.haystack instanceof Object) {
				if (parameters.haystack[element.value] || false) {
					return true;
				}
			}
			return 'notInArray';
		},
		
		ZendX_Validate_InArrayOrEmpty: function(element, parameters) {
			if (App.Forms.Validator.Zend_Validate_NotEmpty(element, parameters) === true)
				return App.Forms.Validator.Zend_Validate_InArray(element, parameters);
			return true;
		},
		
		Zend_Validate_Digits: function(element, parameters)	{
			if (element.value.match(/^[0-9\s]*$/i) !== null)
				return true;
			return 'notDigits';
		},
		
		Zend_Validate_StringLength: function(element, parameters) {
			if (element.value.length > parameters.max)
				return 'stringLengthTooLong';
			if (element.value.length < parameters.min)
				return 'stringLengthTooShort';
			return true;
		},
		
		Zend_Validate_EmailAddress: function(element, parameters) {
			var value 	= element.value;
			
			if (value.match(/^((.+)@([^@]+))?$/i) === null)
				return 'emailAddressInvalid';
			
			if (parameters['userCub'] === undefined || parameters.userCub) {
				var ajaxResponse = jQuery.ajax({
					type: "POST",
					url: App.CUB.urlAjax,
					data: ({
						name:   element.name.replace('[]', ''),
						value:  value,
						numero_form:  App.CUB.numero_form,
						fromJs: 1
					}),
					async: false,
					cache: false
				});
				
				var headers = ajaxResponse['responseHeaders'] || Utile.parseHTTPHeaders(ajaxResponse.getAllResponseHeaders());
				if (headers['X-Cub-Field-Validity'] == 'VALID')
					return true;
				
				var response = ajaxResponse.responseText;
			}
			
			if (parseInt(response) === 0)
				return 'notUniqueEmail';
			else
				return App.Forms.parseMessage(translator['emailAddressInvalidHostname'], value, {
					hostname: value.replace(/^[^@]+@/, '')
				});
		},
		
		Zend_Validate_Regex: function(element, parameters) {
			var regexp = typeof(parameters.pattern) == "string" ? eval(parameters.pattern) : parameters.pattern;
			if (element.value == '' || element.value.match(regexp) !== null)
				return true;
			return 'regexNotMatch';
		},	
		
		Zend_Validate_Identical: function(element, parameters) {
			var tokenElement = document.getElementById(parameters.token);
			if (element.value == tokenElement ? tokenElement.value : '')
				return true;
			return 'notSame';
		},

		ZendX_Validate_DomainName: function(element, parameters) {
			if (!parameters['ajaxMethodUrl'] || parameters['ajaxMethodUrl'] == '') return "Merci de définir l'url de controle AJAX";
			var value = element.value;
			var ajaxResponse = jQuery.ajax({
				type: "POST",
				url: parameters.ajaxMethodUrl,
				data: {
					domainName: value,
					domainType: parameters['hostType'] || 'ANY'
				},
				async: false,
				cache: false
			});
			var answer = ajaxResponse.responseText;
			return answer != 1 ? answer : true;
		},
		
		ZendX_Validate_String: function(element, parameters) {
			return App.Forms.Validator.Zend_Validate_Regex(element, parameters);
		},	
		
		ZendX_Validate_Phone: function(element, parameters) {
			if (App.Forms.Validator.Zend_Validate_Regex(element, parameters) === true)
				return true;
			return 'notPhone';
		},

		ZendX_Validate_SiretSiren: function(element, parameters) {
			if (App.Forms.Validator.Zend_Validate_Regex(element, parameters) !== true)
				return 'notSiretSiren';
			return true;
		},
		
		ZendX_Validate_PostalCode: function(element, parameters) {
			if (App.Forms.Validator.Zend_Validate_Regex(element, parameters) !== true)
				return 'notPostalCode';
			return true;
		},

		ZendX_Validate_Email: function(element, parameters) {
			parameters.pattern = /^([a-zA-Z0-9]+(([\.\-\_]?[a-zA-Z0-9]+)+)?)\@(([a-zA-Z0-9]+[\.\-\_])+[a-zA-Z]{2,4})$/i;
			if (App.Forms.Validator.Zend_Validate_Regex(element, parameters) !== true)
				return 'emailAddressInvalid';
			return true;
		},
		ZendX_Validate_URL: function(element, parameters) {
			if (App.Forms.Validator.Zend_Validate_Regex(element, parameters) !== true)
				return 'notValidURL';
			return true;
		},
		
		ZendX_Validate_Date: function(element, parameters) {
			// FIXME IMPLEMENT DATE CONTROL IN JS
			return true;
		}
	},
	
	Filter : {
		Zend_Filter_Digits: function(value, parameters) {
			return value.replace(/[^\d]/g, "");
		},
		ZendX_Filter_NormalizePhoneNumber: function(value, parameters) {
			return value.replace(/[^\d]+/g, "");
		},
		ZendX_Filter_RemoveSpaces: function(value, parameters) {
			return value.replace(/[\s]+/g, "");
		}
	},
	
	parseMessage: function(message, value, parameters) {
		message = message.replace('%value%', value);
		for (prop in parameters)
			message = message.replace('%'+prop+'%', parameters[prop]);
		return message;
	},
	
	applyTooltips: function(form) {
		
		if (form.nodeName != 'FORM') return;
		
		var formName = form['id'] ? form.id : 'form'; // Contient la liste des champs déjà validés (les noms de champs)
		
		// Aucun control disponible pour le formulaire, il est valide
		if (!App.Forms.FormList[formName]) return true;
		
		for (var i = 0; i < form.elements.length; i++) {
			var field = form.elements[i],
				fieldName = field.name || '';

			if (!field.id) field.id = Utile.randomUID();
			
			var ruleset = App.Forms.FormList[formName][fieldName];
			if (!ruleset) continue;
			if (ruleset['tooltip']) {
				jQuery(field).emptyonclick({
					defaultValue: ruleset['tooltip'],
					defaultClass: 'defaultValue'
				});
			}
		}
	}
}

jQuery.behaviors.register(function(node) {
	// Pas de formulaire ? Pas la peine de continuer
	var jForm = jQuery("form", node);
	if (!jForm.length) return;
	
	jQuery(":input", jForm).change(App.Forms.checkFieldValidity);
	jQuery(".inputText", jForm).blur(App.Forms.checkFieldValidity);
	jForm.each(function() {
		App.Forms.applyTooltips(this);
	});
	jForm.submit(function(event) {
		var formValidity = App.Forms.checkFormValidity(this);
		return formValidity;
	});
	
});

