var iValidator = new Class({
	Implements: Options,

	options:{
		focus: 'focus',
		valid: 'valid',
		invalid: 'invalid',
		norequired: 'norequired',
		blink: 'blink',
		maxlength: true,
		parent: 1,
		tipFade: false
	},

	initialize: function(inputs,options){
		this.setOptions(options);
		this.inputs = [];
		inputs.each(this.addInput,this);
	},

	addInput: function(input){
		this.inputs.push(input.addEvents({focus:this.focus.bind(this),blur:this.blur.bind(this)}));
		if(this.options.maxlength && input.getAttribute('maxlength') && input.getTag()=='textarea'){
			input.addEvent('keyup',function(){
				var maxlength = this.getAttribute('maxlength').toInt();
				if(this.value.length > maxlength){
					this.value = this.value.substr(0,maxlength);
				}
			});
		}
	},
	
	cleanClasses: function(el){
		el.
			removeClass(this.options.focus).
			removeClass(this.options.valid).
			removeClass(this.options.invalid).
			removeClass(this.options.norequired);
	},

	focus: function(e){
		if(!this.disabled){
			this.stopBlink();
			this.hideTip();
			
			var input = new Event(e).target;
			var parent = this.getParent(input);
			this.cleanClasses(parent);
			parent.addClass(this.options.focus);
		}
	},

	blur: function(e){
		if(!this.disabled){
			this.stopBlink();
			this.hideTip();
			
			var input = new Event(e).target;
			var parent = this.getParent(input);
			this.cleanClasses(parent);
			parent.addClass(!input.getProperty('invalid') && ((input.value.trim()=='' && input.hasClass(this.options.norequired)) || input.value.trim().test(iRules[input.className.split(' ')[0]].regx)) ? this.options.valid : this.options.invalid);
		}
	},

	test: function(){
		if(!this.disabled){
			this.status = true;
			this.errors = {};
			this.stopBlink();
			this.hideTip();
			for(var i=0;i<this.inputs.length;i++){
				var parent = this.getParent(this.inputs[i]);
				if(!parent){
					alert(i+' -> '+this.inputs[i].name)
					return;
				}
				
				this.cleanClasses(parent);

				var rule = iRules[this.inputs[i].className.split(' ')[0]];
				var value = this.inputs[i].value.trim();
				if(this.inputs[i].getProperty('invalid') || (value!='' || !this.inputs[i].hasClass(this.options.norequired)) && !value.test(rule.regx)){
					parent.addClass(this.options.invalid);
					this.status=false;
					this.errors[this.inputs[i].name] = rule.msg;
				}else{
					parent.addClass(this.options.valid);
				}
			}
			return this.status;
		}else{
			return null;
		}
	},

	showInvalid: function(blink,tip,showName){
		if(blink!=true){blink=false;}
		if(tip!=true && $type(tip)!='object'){tip=false;}
		for(var i=0;i<this.inputs.length;i++){
			var parent = this.getParent(this.inputs[i]);
			if(parent.hasClass(this.options.invalid)){
				this.cleanClasses(parent);
				if(blink || tip){
					//parpadeo
					if(blink){
						parent.addClass(this.options.blink);
						this.blinker={index: i, timer: parent.toggleClass.periodical(400,parent,[this.options.blink])};
					}
					//tip
					if(tip){
						this.showTip(this.inputs[i],tip,showName);
					}
				}else{
					this.inputs[i].focus();
				}
				break;
			}
		}
	},

	showTip: function(input,tipPos,showName){
		if(!this.tip){
			this.tip = new Element('div',{'class':'iVtip hidden'}).inject(document.body);
			if(this.options.tipFade){
				this.tipFx = new Fx.Tween(this.tip,'opacity');
			}
		}
		var pos = {x:-230,y:-16};
		if(tipPos){
			pos = $merge(pos,tipPos);
		}
		var msg = iRules[input.className.split(' ')[0]].msg;
		if(!$defined(showName) || showName==true){
			var title = '<h3>'+input.name.capitalize()+'</h3>';
		}else if(showName==false){
			var title = '';
		}else{
			var title = '<h3>'+(showName==true ? input.name.capitalize() : showName)+'</h3>';
		}
		//
		this.tip.setHTML('<div>'+title+'<p>'+msg+'</p></div><div class="foot"></div>');
		var position = input.getPosition();
		this.tip.setStyles({'left':(position.x+pos.x)+'px','top':(position.y+pos.y)+'px'});
		//
		if(this.options.tipFade){
			this.tip.setOpacity(0).removeClass('hidden');
			this.tipFx.start(1);
		}else{
			this.tip.removeClass('hidden');
		}
		window.scrollTo(position.x+pos.x,position.y+pos.y);
	},

	hideTip: function(){
		if(this.tip){
			this.tip.addClass('hidden');
		}
	},

	stopBlink: function(){
		if(this.blinker){
			$clear(this.blinker.timer);
			this.getParent(this.inputs[this.blinker.index]).removeClass(this.options.blink);
			delete this.blinker;
		}
	},

	displayErrors: function(box,extra){
		var error = '';
		for(var i in this.errors){
			error += '- '+i.capitalize()+': '+this.errors[i]+'\n';
		}
		if(error!='' || extra){
			var errors = this.errors;
			if(extra){
				errors = $merge(errors,extra);
			}
			if(box){
				box.empty();
				new Element('h3').setText('Debe corregir los datos de los siguientes campos:').inject(box);
				var list = new Element('ul').inject(box);
				for(var i in errors){
					new Element('li').setHTML('<b>'+i.capitalize()+'</b>: '+errors[i]).inject(list);
				}
			}else{
				alert('Debe corregir los datos de los siguientes campos:\n\n'+error);
			}
		}
	},

	getParent: function(el){
		var parent = el;
		for(var i=0;i<this.options.parent;i++){
			parent = parent.getParent();
		}
		return parent;
	}

});