/*

required	The field becomes required.  This is a regex, you can change it with class options.
alpha	The value is restricted to alphabetic chars.  This is a regex, you can change it with class options.
alphanum	The value is restricted to alphanumeric characters only.  This is a regex, you can change it with class options.
nodigit	The field doesnŐt accept digit chars.  This is a regex, you can change it with class options.
digit	The value is restricted to digit (no floating point number) chars, you can pass two arguments (f.e. digit[21,65]) to limit the number between them.  Use -1 as second argument to not set a maximum.
number	The value is restricted to number, including floating point number.  This is a regex, you can change it with class options.
email	The value is restricted to valid email.  This is a regex, you can change it with class options.
phone	The value is restricted to phone chars.  This is a regex, you can change it with class options.
zip     The value is restricted to zip chars
confirm	The value has to be the same as the one passed in argument. f.e. confirm[password].
differs	The value has to be diferent as the one passed in argument. f.e. differs[user].
length

*/
U.Form = new Class({
	Implements : [Events, Options],
	groups : {},
	elements : {},
	showingError : false,
	_uploads : {},
	options : {
		classes : {
			error : 'error',
			info : 'info'
		},
		hasUpload : false,
		doSubmit : true,
		processingText : 'Please wait..',
		validations : {
			required : /[^.*]/,
			alpha : /^[a-z ._-]+$/i,
			alphanum : /^[a-z0-9 ._-]+$/i,
			digit : /^[-+]?[0-9]+$/,
			nodigit : /^[^0-9]+$/,
			number : /^[-+]?\d*\.?\d+$/,
			email : /^[a-z0-9._%-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i,
			phone : /^[\d\s ().-]+$///,
			//url : /^(http|https|ftp)\:\/\/[a-z0-9\-\.]+\.[a-z]{2,3}(:[a-z0-9]*)?\/?([a-z0-9\-\._\?\,\'\/\\\+&amp;%\$#\=~])*$/i
		},
		errors : {
			digit: "Please enter a valid integer.",
			digitmin: "The number must be at least {0}",
			digitrange: "The value must be between {0} and {1}"	,
			length : "Minimum {0} characters please",						
			rangelength: "Between {0} and {1} characters please",
			fixlength: "It must be exactly {0} characters please",
			maxlength: "Max {0} characters please",
			minlength: "Minimum {0} characters please",
			uploadInProgress : "Please wait for the other upload to finish",
			containerTemplate : '<ul>{0}</ul>',
			itemTemplate : '<li>{0}</li>'
		},
		rowTag : 'li',
		errorBoxSelector : '.message-container',
		showErrors : true
	},
	initialize : function(element, options) {
		this.element = $(element);
		this.setOptions(options);
		var messageBox = this.element.getElements(this.options.errorBoxSelector)[0];
		this.messageBox = new U.Form.MessageBox(messageBox);
		var infoBox = messageBox.clone().inject(messageBox.parentNode);
		this.infoBox = new U.Form.MessageBox(infoBox).addEvents({
			show : this._showError.bind(this, false),
			hide : this._showError.bind(this, true)
		});
		if (this.options.hasUpload) {
			this.fileBox = new U.Form.MessageBox(messageBox.clone().inject(messageBox.parentNode));
		}
		
		if (this.element == null) {
			alert('You must pass a valid form element');
			return;
		}
		$A(this.element.elements).each(
			function(item, index) {
				item = $(item);
				var rules = item.get('validate');
				var elmType = item.type;
				//item.set('tabindex', index);
				if (rules == null && elmType != 'file') return;
				eval("rules = "+rules);
				//rules.parent = ;
				//rules.required = true;
				if (rules && !this.groups[rules.group]) this.groups[rules.group] = {};
				$log('elmType = '+elmType)
				if (!elmType.containedIn('submit,file')) {
				//$log('elmType = '+elmType)
					var formItem = this.groups[rules.group][item.id] = new U.Form.Item(item, rules, item.getParent(this.options.rowTag));
					this.elements[item.name] = formItem;
					formItem.addValidation({type:'required'});
					for(var i in rules) {
						if (!i.containedIn('msg,group,info,wait')) {
							formItem.addValidation({type:i,rule:rules[i]});
						} else if (i == 'info') {
							item.addEvent('focus', this._showInfo.bind(this, [formItem, true]));
							item.addEvent('blur', this._showInfo.bind(this, [formItem, false]));
						}
					}
					this._addItem(formItem);
				} else if (elmType == 'submit') {
					item.store('processingText', $pick(rules.processingText, this.options.processingText)).store('defaultText', item.value);
					item.addEvent('click', this._validate.bind(this, [rules.group, item]));
				} else if (elmType == 'file' && this.options.hasUpload) {
					this._uploads[item.id] = new U.UI.FileUpload(item).addEvents({
						//start : this._disableUploads.bind(this, true),
						fail : this._fileUploadError.bind(this),
						complete : this._fileUploadDone.bind(this)/*,
						disabledClick : this._fileUploadDisabledClicked.bind(this)*/
					});
				}
				
			}, this
		);
		this.element.addEvent('submit', this._onSubmit.bind(this));
	},
	_addItem : function(item) {
		if (!this._isChildElement(item)) {
			item.element.addEvent('blur', this._validateItem.bind(this, item));
			//item.element.addEvent('blur', function (obj) {this._validateItem.delay(100, this, obj)}.bind(this, item));
		}
	},
	_onSubmit : function(event) {
		new Event(event).stop();
		//if (!ok) 
	},
	_showInfo : function(item, show) {
		if (show) {
			this.infoBox.showInfo(item.getMessage('info'), item.getParent())
		} else {
			this.infoBox.hide();
		}
	},
	_showError : function(show) {
		if (this.showingError) {
			this.messageBox[show?'show':'hide']();
		}
	},
	_validate : function(group, btn) {
		var groups = group.split(',');
		$log(groups[1])
		//var group = this.groups[this.currGroup];
		var ok = true;
		for (var i=0,nL=groups.length,group;i<nL;i++) {
			group = this.groups[groups[i]];
			$log('group='+groups[i])
			for(var item in group) {
				$log('group item =' +item)
				ok = this._validateItem(group[item]);
				(function() {if (this.currErrorItem) this.currErrorItem.element.focus()}).delay(100,this);
				if (!ok) return false;
			}
		}
		btn.addClass('processing')
		btn.value = btn.retrieve('processingText');
		btn.disabled = true;
		if (this.options.doSubmit) this.element.submit();
		else this.fireEvent('submit');
	},
	getCurrentError : function() {
		return this.currErrorItem;
	},
	_validateItem : function(item) {
		if (this.showingError && item != this.currErrorItem) return false;
		if (!item.element.visible() && item.element.type != 'password') return true;
		var validations = item.getValidations();
		var ok = true, type = 'required', msg = '';
		if (!this._isChildElement(item)) {
			for(var i=0,nL=validations.length, validation, regex, value;i<nL;i++) {
				validation = validations[i];
				type = validation.type;
				regex = this.options.validations[type];
				if (type == 'length') {
					var val = this.options.validations, rule = validation.rule, err = this.options.errors;
					if (rule) {
						if (rule[1]) {
							if (rule[1] == -1) {
								regex = new RegExp("^[\\s\\S]{"+ rule[0] +",}$");
								msg = err.minlength.formatWith(rule[0]);
							} else if (rule[0] == rule[1]) {
								regex = new RegExp("^[\\s\\S]{"+ rule[0] +"}$");
								msg = err.fixlength.formatWith(rule[0]);
							} else {
								regex = new RegExp("^[\\s\\S]{"+ rule[0] +","+ rule[1] +"}$");
								msg = err.rangelength.formatWith(rule[0], rule[1]);
							}
						} else if (rule[0]) {
							regex = new RegExp("^.{0,"+ rule[0] +"}$");
							msg = err.maxlength.formatWith(rule[0]);
						}
					}
				}
				
				value = item.getValue();
				//$log(validations[i]+' test = '+validation.test(item.getValue())+' value = '+item.getValue())
				if (!regex.test(value)) {
					ok = false;
					break;
				} else if (type == 'digit'){
					if (validation.rule && validation.rule.length) {
						if (value < validation.rule[0]) {
							ok = false;
							msg = this.options.errors.digitmin.formatWith(validation.rule[0]);
							break;
						} else if (value >= validation.rule[0] && (value > validation.rule[1] && validation.rule[1] != -1)) {
							ok = false;
							msg = this.options.errors.digitrange.formatWith(validation.rule[0], validation.rule[1]);
							break;
						}
					}
				}
			}
		} else ok = this._validateItemGroup(item);
		if (!ok) {
			//$log(item.element.id+' '+item.element.focus);
			
			this.messageBox.showError((msg == '')?item.getMessage():msg, item.getParent());
			//$(item.element.id).focus.delay(100);
			this.showingError = true;
			this.currErrorItem = item;
			item.showError();
			//item.element.addClass('error');
			return false;
		} else {
			this.clearError();
			return true;
		}
		return true;
	},
	clearError : function() {
		if (!this.currErrorItem) return;
		this.messageBox.hide();
		//this.currErrorItem.element.removeClass('error');
		this.currErrorItem.hideError();
		this.showingError = this.currErrorItem = false;	
	},
	//_focusCurrent : function() {this.currErrorItem.element.focus()},
	_isChildElement: function(item) {
		return item.element.type.toLowerCase() == 'radio';
	},
	_validateItemGroup : function(item) {
		var group = this.element[item.element.get("name")];
		
		for(var i = 0, nL = group.length; i < nL; i++) {
			if(group[i].checked) {
				return true;
			}
		}
		return false;
	},
	_disableUploads : function(disable) {
		for(var i in this._uploads) {
			this._uploads[i][disable?'disable':'enable']();
		}
	},
	_fileUploadError : function(obj, message) {
		$log('_fileUploadError message = '+message)
		this.fileBox.showError(message, obj.container);
		//this._disableUploads(false);
	},
	_fileUploadDone : function(obj, data) {
		$log('_fileUploadDone')
		this.fireEvent('uploaddone', data);
		//this._disableUploads(false);
	},
	_fileUploadDisabledClicked : function(obj) {
		this.fileBox.showError(this.options.errors.uploadInProgress, obj.container);
	},
	showErrors : function(errors) {
		var error;var output = "";
		for (var i in errors) {
			this.elements[i].showError();
			output+=this.options.errors.itemTemplate.formatWith(errors[i]);
		}
		if (!this.ajaxError) this.ajaxError = $(this.element.id+'-ajax-error');
		this.ajaxError.innerHTML = this.options.errors.containerTemplate.formatWith(output);
	}
});
U.Form.Item = new Class({
	validations : [],
	initialize : function(element, rules, parent) {
		this.element = element;
		this.isPassword = false;
		//$log(this.element.type+' '+this.element.get('tag'))
		if (this.element.type == 'text' || this.element.get('tag') == 'textarea') {
			this.textBox = new U.UI.TextBox(this.element);
		} else if (this.element.type == 'password') {
			this.textBox = new U.UI.Password(this.element);
			this.isPassword = true;
		}
		this.message = rules.msg;
		this.info = rules.info;
		this.wait = rules.wait;
		//$log("parent = "+parent);
		this.parent = parent;
	},
	getValue : function() {
		//$log(this.element.options+' '+this.element.value+' '+this.element.get('tag'));
		if (this.textBox) {
			//$log('value = '+this.textBox.getValue())
			return this.textBox.getValue();
		}
		else if (!this.element.options) return this.element.value;
		else {
			var value = this.element.getSelected().get('value')[0];
			if (value == '-1') return '';
		}
	},
	getParent : function() {return this.parent},
	addValidation : function(validation) {
		this.validations.push(validation);
	},
	getValidations : function() {return this.validations},
	// you can pass in 'info', 'error', 'wait'
	getMessage : function(type) {
		if (!type) return this.message;
		else return this[type];
	},
	showError : function() {
		this.element.addClass('error');
		if (this.isPassword) this.textBox.text.addClass('error');
	},
	hideError : function() {
		this.element.removeClass('error');
		if (this.isPassword) this.textBox.text.removeClass('error');
	}
});
U.Form.MessageBox = new Class({
	Implements : Events,
	initialize : function(element) {
		this.element = $(element);
		this.message = this.element.getElements('.message')[0];
		this.currentClass = 'error';
		this.hidden = true;
	},
	_show : function(message, where, className) {
		if (this.hidden) {
			this.element.show();
			this.hidden = false;
		}
		this.element.fade('hide');
		this.message.set('html', message);
		this.element.removeClass(this.currentClass)
		this.element.addClass(this.currentClass = className);
		this.element.inject(where);
		this.fireEvent('show');
		this.show();
	},
	showError : function(message, where) {
		this._show(message, where, 'message-error');
	},
	showInfo : function(message, where) {
		this._show(message, where, 'message-info');
	},
	showWait : function(message, where) {
		this._show(message, where, 'message-wait');
	},
	show : function() {
		$log('showing');
		//this.element.fade('in');
		this.element.fade(0.9);
	},
	hide : function() {
		$log('hiding')
		this.element.fade('out');
		this.fireEvent('hide');
	}
});