var Validator = new Class({
	Implements: Options,
	options: {
		invalidClass: 'invalid',
		validClass: '',
		requiredClass: 'required',
		ignoredClass: 'ignored',
		ajaxCall: true,
		validateOn: 'blur', //'keyup', 'submit'
		ajaxSendOptions: null,
		onInvalidField: null,
		onValidField: null,
		onInvalidForm: null,
		onFieldFocus: null,
		rules: {
			email: function(value, field, opt){return value.test(/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i);},
			url: function(value, field, opt){return value.test(/^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i);},
			alpha: function(value, field, opt){return value.test(/^[a-zA-Z]+$/);},
			numeric: function(value, field, opt){return value.test(/^[\d]+$/);},
			'alpha-num': function(value, field, opt){return value.test(/\w/);},
			maxlength: function(value, field, opt){return (value.length <= opt[0]);},
			minlength: function(value, field, opt){return (value.length >= opt[0]);},
			min: function(value, field, opt){return (value.toInt() >= opt[0]);},
			max: function(value, field, opt){return (value.toInt() <= opt[0]);},
			range: function(value, field, opt){var v = value.toInt(); return (v >= opt[0] && v <= opt[1]);},
			differs: function(value, field, opt){return !opt.contains(value);},
			reqexp: function(value, field, opt){return value.test(new RegExp(opt[0]));}
		},
		messages: {
			required: 'Field is required',
			email: 'Email must be something like: validator@sevenspire.com',
			url: 'Invalid URL (Example: http://www.sevenspire.com)',
			alpha: 'Letters only!',
			numeric: 'Sorry, please only digits!',
			'alpha-num': 'Please only alpha-numerics',
			maxlength: 'ToooOO Long!',
			minlength: 'ToooOO Short!',
			min: 'Number to small!',
			max: 'Number to BIG!',
			range: 'Number not in range!',
			differs: 'Value must differ',
			regexp: 'Don\'t match the given regex'
		},
		msgTpl: ''
	},
	
	initialize: function(form, options){
		if ($(form)) {
			this.setOptions(options);
			this.options.rules = new Hash(this.options.rules);
			this.options.messages = new Hash(this.options.messages);
			
			!this.options.onInvalidField ? this.options.onInvalidField = this.onInvalidField : null;
			!this.options.onValidField ? this.options.onValidField = this.onValidField : null;
			!this.options.onInvalidForm ? this.options.onInvalidForm = this.onInvalidForm : null;
			!this.options.onFieldFocus ? this.options.onFieldFocus = this.onFieldFocus : null;
			
			this.form = $(form);
			this.fields = this.form.getElements('input, textarea, select').filter(function(item){
				return !(item.match('input[type=button]') || item.match('input[type=image]') || item.match('input[type=submit]') || item.match('*[class~=' + this.options.ignoredClass + ']'));
			}.bind(this));
			
			
			this.fields.each(function(field){
				field = $(field);
				field.rules = this.parseRules(field.get('class'));
				
				field.addEvent('focus', function(e){
					this.options.onFieldFocus.run([e, field], this);
				}.bindWithEvent(this));
				
				if (this.options.validateOn!='submit') {
					field.addEvent(this.options.validateOn, function(e){
						this.validateField(field);
					}.bindWithEvent(this));
				}
			}.bind(this));
			
			this.form.addEvent('submit', function(e){
				e?e.stop():null;
				
				this.validate();
		
				if (this.valid) {
					if (this.options.ajaxCall) {
						this.form.set('send', this.options.ajaxSendOptions).send();
					}
					else {
						this.form.submit();
					}
				}
			}.bindWithEvent(this));
		}
	},
	
	isEmpty: function(field){
			switch (field.type) {
				case 'text':
				case 'password':
				case 'file':
				case 'textarea':
				case 'select-one':
					return (field.get('value').trim() == '');
					break;
				case 'select-multiple':
					return (field.get('value').length==0);
					break;
				case 'radio':
				case 'checkbox':
					return (field.get('value')===false?true:false);
					break;
			}
			return false;
	},
	
	validateField: function(field){
		field.valid = true;
		field.messages = new Array();
		if (this.isEmpty(field)) {
			if (field.hasClass(this.options.requiredClass)) {
				field.valid = false;
				field.messages.push(this.options.messages.get(this.options.requiredClass));
				this.options.onInvalidField.run(field, this);
			}
			return field.valid;
		}
				
		switch (field.type) {
			case 'text':
			case 'password':
			case 'file':
			case 'textarea':
			case 'select-one':
				this.options.rules.each(function(rule, key){
					if (field.rules.has(key)) {
						var ruleValid = rule(field.get('value'), field, field.rules.get(key));
						field.valid = (field.valid?ruleValid:false);
						ruleValid ? null : field.messages.push(this.options.messages.get(key));
					}
				}.bind(this));

				if (!field.valid) {
					this.options.onInvalidField.run(field, this);
				}
				else {
					this.options.onValidField.run(field, this);
				}
				break;
			case 'radio':
			case 'checkbox':
				break;
			case 'select-multiple':
				break;
		}
			
		return field.valid;
	},
	
	validate: function(){
		this.valid = true;
		this.fields.each(function(field){
			if (field) {
				var fieldValid = this.validateField(field);
				this.valid = (this.valid ? fieldValid : false);
			}
		}.bind(this));
		if(this.valid){
			
		}
		else{
			this.options.onInvalidForm.run([], this);
		}
		return this.valid;	
	},
	
	onInvalidField: function(field){
		field.getParent().getElements('span.error-cont').destroy();
		field.removeClass(this.options.validClass).addClass(this.options.invalidClass);

		field.messages.each(function(msg){
			new Element('label', {
				'id': $pick(field.get('id'), field.get('name'), field.get('tag')) + '_label',
				'class': 'error',
				'for': field.get('id'),
				'text': msg
			}).inject(new Element('span', {
				'class': 'error-cont'
			}).inject(field, 'after').fade('hide').fade('in'));
		});
	},
	
	onValidField: function(field){
		field.getParent().getElements('span.error-cont').destroy();
		field.removeClass(this.options.invalidClass).addClass(this.options.validClass);
	},
	
	onInvalidForm: function(){
		this.fields.each(function(field){
			if(!field.valid){
				this.options.onInvalidField.run(field, this);
			}
		}.bind(this));
	},
	
	onFieldFocus: function(e, field){
		field.removeClass(this.options.invalidClass);
	},
	
	parseRule: function(rule){
		var re = /\([^()]*\)/;
		return rule.match(re)[0].replace(/[\(\)]/g, '').split(',');
	},
	
	parseRules: function(rules){
		var els = rules.split(' ');
		var rules = new Hash();
		
		els.each(function(el){
			rules.set(this.removeOpts(el), el.contains('(')?this.parseRule(el):[]);
		}.bind(this));
		return rules;
	},
	
	removeOpts: function(rule){
		while (rule != (rule = rule.replace(/\([^\(\)]*\)/g, "")));
		return rule;
	}
});
