/*
Validation structure:

validations = [
	// Structure
	{name: 'serialized_field_name', rule: 'string_or_function', params: 'optional_params'},
	// Samples
	{name: 'email', rule: 'required'},
	// Using validation parameters
	{name: 'age', rule: 'in_range', params: [0, 150]},
]
*/


(function($){
	
	$.v7r = {
		
		/*
		VALIDATION RULES
		
		Every rule receives at least two arguments: the serialized value for
		the current field name, and the full list of values as a dictionary.
		
		Any rule-specific parameter will be appended after the default parameters.
		*/
		
		rules: {
			required: function (v) {
				return (v != '') && (v != null);
			},
			is_email: function (v) {
				if (v == '') return true;
				return /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/i.test(v);
			},
			matches: function (v, value_list, other) {
				if (v == '') return true;
				return (v == value_list[other]);
			},
			required_with: function (v, value_list, other) {
				if (!$.v7r.rules.required(value_list[other])) return true;
				return $.v7r.rules.required(v);
			},
			is_number: function(v){
				if (v == '') return true;
				return !isNaN(v);
			},
			is_float: function(v){
				if (v == '') return true;
				/* anderson: incluido substituição de ponto por nada */
				v = v.replace(/\./g,"");
				/* if (v.indexOf('.') >= 0) return false; */
				v = v.replace(',', '.');
				return !isNaN(v);
			},
			min_length: function(v, value_list, len){
				if (v == '') return true;
				return (v.length >= len);
			},
			is_date: function (v) {
				
				if (v == '') return true;
				
				// Separa dia, m�s e ano da data e invalida se n�o houver as tr�s
				var parts = v.split('/');
				if (parts.length != 3) {
					return false;
				}
				
				// Atalhos para as partes da data
				// Nota: meses em datas no Javascript s�o tratados com in�cio em 0,
				// ou seja, Janeiro=0 e Dezembro=11
				var dd = parts[0], mm = parts[1] - 1, yy = parts[2];
				
				// Invalida se o ano for menor que 1900
				if (yy < 1900) {
					return false;
				}
				
				// Verifica se os valores do objeto de data batem com os valores informados
				var dt = new Date(yy, mm, dd);
				return (
					(yy == dt.getFullYear()) &&
					(mm == dt.getMonth()) &&
					(dd == dt.getDate())
				);
				
			}
		},
		
		// Customizable options
		options: {
			// Error cleaning method
			clear: null,
			// Error reporting method
			error: function (errors) {
				var error_names = $.map(errors, function(elem){
					return elem.name;
				});
				
				alert('Invalid fields: ' + error_names.join(', '));
			}
		},
		
		// Rule testing
		test: function (rule, value, value_list, rule_params) {
			
			// The rule isn't a function?
			if (!$.isFunction(rule)) {
				// Look for an internal validation rule, using the rule parameter as key name
				rule = $.v7r.rules[rule];
				// No rule found? Invalidate!
				if (!rule) return false;
			}
			
			// Initialize the test parameters with the field value and the values list
			params = [value, value_list];
			
			// Add rule parameters
			$.merge(params, (rule_params ? rule_params : []));
			
			// Test the value against the rule
			return rule.apply(null, params);
			
		},
		
		// Test if the object is an array
		is_array: function (elem) {
			return elem.length && elem[elem.length - 1] !== undefined && !elem.nodeType;
		},
		
		// Custom serialization
		serialize: function (context) {
			
			var value_list = {};
			
			// Convert the serialized object to a dictionary (key/value pairs)
			// TODO: deal with repeated values (multi-selects or checkboxes)
			$.each($(context).serializeArray(), function(){
				
				// Look for fields with the same name
				var fields = $('[name=' + this.name + ']');
				
				// If is a select-multiple field, or a group of checkboxes
				if (fields.is('[multiple]') || (fields.length > 1) && fields.is(':checkbox')) {
					// Append the value to the array if no value found
					// or create a new array with the first value
					if (value_list[this.name]) {
						value_list[this.name].push(this.value);
					}
					else {
						value_list[this.name] = [this.value];
					}
				}
				// Simple field? Just assign the value
				else {
					value_list[this.name] = this.value;
				}
				
			});
			
			return value_list;
			
		},
		
		has_errors: function (context, validations, options) {
			
			// Extend the default options
			var options = $.extend($.v7r.options, options);
			
			// Run the cleaning function for the context
			if ($.isFunction(options.clear)) {
				options.clear.call(context);
			}
			
			// Error and value lists
			var errors = [];
			var value_list = $.v7r.serialize(context);
			
			// Test each validation rule
			$.each(validations, function () {
				if (!$.v7r.test(this.rule, value_list[this.name], value_list, this.params)) {
					// Add the rule to the error list if failed the test
					errors.push(this);
				}
			});
			
			// If errors found
			if (errors.length > 0) {
				// Run the error callback (if it is a valid function)
				if ($.isFunction(options.error)) {
					options.error.call(context, errors);
				}
				
				// Return the list of failed validations
				return errors;
			}
			else {
				// No errors
				return false;
			}
			
		}
	};
	
	$.fn.has_errors = function (validations, options) {
		return $.v7r.has_errors(this, validations, options);
	};
	
})(jQuery);
