// form parsing
var FormValidation = new Class({
	// constructor supports up to 4 arguments
	// id of form (mandatory)
	// message to alert on validation failure (not mandatory)
	// liveUpdate - weather to change the status of each field while typing / onchange (not mandatory, default to true)
	// custom error field mark up
  	initialize: function(name) {
		this.form = $(name);
		this.rules = {};
		this.msg = arguments[1] != null ? arguments[1] : null;
		this.liveUpdate = arguments[2] != null ? arguments[2] : true;
		this.markMode = arguments[3] != null ? arguments[3] : null;
	},
	
	// define or remove filters for a field
	setRule: function(elem) {
		// no rules sent exit
		if (arguments.length != 2) return;
		
		// if field does not exist don't create rule
		if (this.form.elements[elem] == null) return;
		this.rules[elem] = arguments[1];
		
		// add event handlers for live update
		if (this.liveUpdate) {
			if (this.form.elements[elem].type == 'select-one' || this.form.elements[elem].type == 'checkbox')
				$(this.form.elements[elem]).addEvent('change', this.updateElement.bind(this));
			else if (this.form.elements[elem].type == 'checkbox')
				$(this.form.elements[elem]).addEvent('click', this.updateElement.bind(this));
			else
				$(this.form.elements[elem]).addEvent('keyup', this.updateElement.bind(this));			
		}
	},
	
	// remove filters: all of a form, all of an element or one of an element
	removeRule: function() {
		if (arguments.length == 0) {
			// no parameter - remove all form filters
			this.unmarkFields();
			this.rules = {};
		}
		else {
			if (arguments.length == 1) {
				// remover all filters of an element
				elem = arguments[0];
				if (this.rules[elem] != null) { 
					delete this.rules[elem];
					this.unmarkField(elem);
				}
			}
			else {
				// remover a filter of an element
				elem = arguments[0];
				for (filter in arguments[1]) 
					if (this.rules[elem][filter] != null) { 
						delete this.rules[elem][filter];
						this.unmarkField(elem);
					}
				if (this.countProperties(this.rules[elem]) == 0) delete this.rules[elem];
			}
		}
	},
	
	// check form based on defined filters
	testForm: function(doSubmit) {
		var testForm = true;
		
		var testElements = {};
		// check each element that has a rule
		for (elem in this.rules) {
			testElem = true;
			// check each filter of current element
			for (filter in this.rules[elem]) {
				// aply rule
				testFilter = this.testRule(this.form.elements[elem], filter, this.rules[elem][filter]);
				
				if (!testFilter && testForm) testForm = false;
				if (!testFilter && testElem) testElem = false;

				// if one filter fails don't check the others
				if (!testElem) break;
			}
			// store each elements validation status
			testElements[elem] = testElem;
		}
		
		// if all fields passed validation and in submit mode send form
		if (doSubmit && testForm) {
			this.form.submit();
			return;
		}
		else
			if (!testForm && this.msg && (arguments[1] == null || arguments[1]))
				alert(this.msg);
				
		// if at least one check has failed mark all elements that have rules according to validation status
		if (!testForm)
			for (elem in this.rules)
				this.markField(elem, testElements[elem]);
				
		return testForm;
	},
	
	// check a value against a filter and its parameters
	testRule: function(elem, filter, params) {
		var test = new DataValidation(elem);
		try { eval('val = test.is' + filter + '(params)'); }
		catch(e) { val = true; }
		return val;
	},
	
	markField: function(elem, validated) {
		// create DOM element if not created
		if (this.markMode != null) {
			eval('this.markField' + this.markMode + '(elem, validated)');
			return;
		}
		
		var id = elem + '_status';
		
		if ($(id) == null) {
			$(document.body).inject(new Element('div', { 'id': id }));

			// get coordinates of status image based on element's position
			pos = this.form.elements[elem].getPosition();
			width = this.form.elements[elem].getSize().x;
			pos_top = pos.y + 2 + 'px';
			pos_left = pos.x + width + 5 + 'px';
			$(id).setStyles({ position: 'absolute', top: pos_top, left: pos_left });
		}
		
		$(id).innerHTML('<img src="images/valid_' + (validated ? 'ok' : 'ko') + '.gif">');
	},
	
	markField1: function(elem, validated) {
		// just change background
		if (validated) {
			if (this.form.elements[elem].getStyle('background-color') != '#ffffff')
				this.form.elements[elem].morph({ 'background-color': '#ffffff' });
		}
		else
			if (this.form.elements[elem].getStyle('background-color') != '#ffffe0') {
				this.form.elements[elem].setStyle('background-color', '#ffffff');
				this.form.elements[elem].morph({ 'background-color': '#ffffe0' });
			}
	},
		
	// unmark an element (when filter is removed)
	unmarkField: function(elem) {
		this.markField(elem, true);
	},
	
	// unmark all elements (when all filters are removed)
	unmarkFields: function() {
		for (elem in this.rules)
			this.unmarkField(elem);
	},
	
	// count properties of an object
	countProperties: function(obj) {
		var i = 0;
		for (prop in obj) i++;
		return(i);
	},
	
	// check element on keyup / change
	updateElement: function(e) {
		var elem = e.target.name;
		
		// check if element has rules
		if (this.rules[elem] == null) return;
			
		var testElem = true;
		// check each filter of element
		for (filter in this.rules[elem]) {
			if (filter == 'AjaxValid') continue;
			// aply rule
			testFilter = this.testRule(this.form.elements[elem], filter, this.rules[elem][filter]);

			if (!testFilter && testElem) testElem = false;

			// if one filter fails don't check the others
			if (!testElem) break;
		}
		
		// special live check for ajax filter
		if (testElem && this.rules[elem]['AjaxValid'] != null) {
			var test = new DataValidation(this.form.elements[elem]);
			this.rules[elem]['AjaxValid'].form = this;
			this.rules[elem]['AjaxValid'].elem = elem;
			test.isAjaxValidAsync(this.rules[elem]['AjaxValid']);
		}
		else
			this.markField(elem, testElem);
	},
	
	// debug
	debug: function() {
		var ret = '';
		for (elem in this.rules) {
			ret += elem + '\n';
			for (filter in this.rules[elem]) {
				ret += '  - ' + filter + ': ' + this.rules[elem][filter];
				ret += '\n';
			}
		}
		
		ret += '\n';
		ret += 'Msg: ' + this.msg + '\n';
		ret += 'Live update: ' + this.liveUpdate + '\n';
		ret += 'Mark mode: ' + this.markMode + '\n';

		alert(ret);
	}
})
