var FormValidator = new Class({
	
	Extends: Options,
	
	options: {
		fields: null,
		msgErr: 'Please check highlighted fields !',
		msgErrFinal: null,
		liveMode: true,
		colorBg: '#ffeeee',
		colorBorder: '#8b4513'
	},
	
  initialize: function(form, options) {
		this.form = typeof(form) == 'string' ? $(form) : form;
		this.setOptions(options);
		this.validator = new DataValidator;
		
		if (this.options.liveMode)
			this.bindEvents();
	},
	
	addRules: function(fields) {
		if (!this.options.fields) this.options.fields = {};
		for (var field in fields) {
			if (!this.options.fields[field]) this.options.fields[field] = {};
			for (var rule in fields[field])
				this.options.fields[field][rule] = fields[field][rule];
		}
		this.bindEvents();
	},
	
	removeRules: function(fields) {
		if (fields == null)
			this.options.fields = null;
		else {
			for (var field in fields) {
				if (this.options.fields[field] == null) continue;
				if (fields[field] == null)
					delete this.options.fields[field];
				else {
					fields[field].each(function(rule) {
						delete this.options.fields[field][rule];
					}, this);
					if (this.isObjEmpty(this.options.fields[field]))
						delete this.options.fields[field];
				}
			}
			if (this.isObjEmpty(this.options.fields))
				this.options.fields = null;
		}
		this.unbindEvents(fields);
	},
	
	bindEvents: function() {
		for (var field in this.options.fields) {
			var elem = $(this.form.elements[field]);
			if (elem == null || elem.retrieve('fvBound', null) != null) continue;
			
			var event = 'keyup';
			if (elem.type == 'select-one') event = 'change';
			if (elem.boundCheckField == null)
				elem.boundCheckField = this.checkField.bind(this, elem);
			elem.addEvent(event, elem.boundCheckField);
			elem.store('fvBound', true);
		}
	},
	
	unbindEvents: function(fields) {
		var unbindFields = fields == null ? this.options.fields : fields;
		for (var field in unbindFields) {
			var elem = $(this.form.elements[field]);
			if (elem == null || elem.retrieve('fvBound', null) == null) continue;
			
			var event = 'keyup';
			if (elem.type == 'select-one') event = 'change';
			elem.removeEvent(event, elem.boundCheckField);
			elem.store('fvBound', null);
			
			this.updateField(elem, true);
		}
	},
	
	checkForm: function() {
		this.options.msgErrFinal = null;
		
		var isFormValid = true;
		for (var field in this.options.fields) {
			var elem = $(this.form.elements[field]);
			if (elem == null && typeof(this.form.elements[field]) != 'undefined')
				elem = $(this.form.elements[field][0]);
			if (elem == null) continue;
			isFormValid = this.checkField(elem) && isFormValid;
		}
		
		if (!isFormValid)
			alert(this.options.msgErrFinal ? this.options.msgErrFinal : this.options.msgErr);
			
		return isFormValid;
	},
	
	checkField: function(elem) {
		var field = elem.name;
		var isFieldValid = true;
		for (var rule in this.options.fields[field])
			if (!this.validator.check(elem, rule, this.options.fields[field][rule])) {
				isFieldValid = false;
				break;
			}
		this.updateField(elem, isFieldValid);
		
		return isFieldValid;
	},
	
	updateField: function(elem, isFieldValid) {
		if (elem.type == 'checkbox' || elem.type == 'radio') {
			if (!isFieldValid && this.options.msgErrFinal == null && elem.title)
				this.options.msgErrFinal = elem.title.replace(/<nl>/, '\n');
			return;
		}
		else
		if (!isFieldValid && this.options.msgErrFinal == null)
			this.options.msgErrFinal = this.options.msgErr;
		
		var state = elem.retrieve('state', null);
		if ((state == null && isFieldValid) || (state != null && state == isFieldValid)) return;
		if (state == null) {
			elem.colorBorder = elem.getStyle('border-color').replace(/\s.*/, '');
			elem.colorBg = elem.getStyle('background-color');
		}
		elem.store('state', isFieldValid);
		if (elem.fx == null) elem.fx = new Fx.Morph(elem, { link: 'cancel', duration: 'short' });
		if (isFieldValid)
			elem.fx.start({ 'background-color': elem.colorBg, 'border-color': elem.colorBorder });
		else
			elem.fx.start({ 'background-color': this.options.colorBg, 'border-color': this.options.colorBorder });
	},
	
	debug: function() {
		console.log('Rules:');
		for (var field in this.options.fields)
			console.log('  -', field, this.options.fields[field]);
	},
	
	isObjEmpty: function(obj) {
		for (var key in obj) return false;
		return true;
	}
	
});