/**
 * UIErrorField
 * @author Charles Demers
 * @version 0.1
 * @requires JSForm
 * @requires Core.js
 * @requires jQuery (http://www.jquery.com)
 */

/**
 * @constructor UIErrorField
 * @param {JSForm} 		form						The form you want to bind with the errorfield
 * @param {Object} 		opts
 * @param {String} 		opts.messageContainer		The container(s) where the message(s) will be output. It can be absolute i.e. #foo or relative to the input i.e. {input}.parent("div")
 * @param {String}		opts.errorClass				The class you want to apply to the element(s) specified in opts.errorElement<br />
 *													<strong>Default:</strong> error
 * @param {String}		opts.errorElement			The element(s) on which the errorClass will be applied. It can be absolute i.e. #foo or relative to the input i.e. {input}.parent("div")
 * @param {String}		opts.wrapper				A wrapper element for each error messages<br />
 * 													<strong>Default:</strong> &lt;div&gt;
 *
@example
<strong>Example:</strong><br />
<pre>
var errorField = new UIErrorField(form,{
	messageContainer:'{input}.parent("div")',
	errorClass:"error",
	errorElement:"{input}",
	wrapper:"<p class='msgError'>"
});
</pre>
 */
function UIErrorField(form,opts){
	this.$type = "UIErrorField";
	
	if(form.getErrors == undefined){
		throw new Error("[UIErrorField] form is undefined");
	} else {
		this.form = form;
	}
	
	if(opts && opts.messageContainer){
		if(opts.messageContainer.indexOf("{input}") == 0){
			this.type = "single";
			this.container = opts.messageContainer.substring(7,opts.messageContainer.length);
		} else {
			this.type = "general";
			this.container = opts.messageContainer;
		}
	} else {
		throw new Error("[UIErrorField] messageContainer is not specified");
	}
	
	if(opts && opts.errorElement){
		if(opts.errorElement.indexOf("{input}") == 0){
			this.errorElement = {element:opts.errorElement,type:"relative"};
		} else {
			this.errorElement = {element:opts.errorElement,type:"absolute"};
		}
	} else {
		this.errorElement = {element:this.container};
		this.errorElement.type = (this.type == "single") ? "relative" : "absolute";
	}
	this.limit = (this.type == "general" && opts && typeOf(opts.limit) !== "undefined") ? opts.limit : 0;
	this.errorClass = (opts && opts.errorClass) ? opts.errorClass : "error";
	this.wrapper = (opts && typeOf(opts.wrapper) !== "undefined") ? opts.wrapper : "<div>";
	this.messages = [];
	this.errorClassedElements = [];
	
	this._argsRegExp = /[\(][\'\"]?[\w]+[\'\"]?[\)]/;
	this._funcRegExp = /\.[\w]+[\(][\'\"]?[\w]+[\'\"]?[\)]/g;
}
UIErrorField.implement({
	/**
	 * Adds the error messages to the DOM
	 */
	showErrors:function(){
		var errors = this.form.getErrors();
		var content,i;
		this.removeErrors();
		if(this.type == "single"){
			for(var p in errors){
				if(errors[p].length > 0){
					var el = jQuery(this.form.inputs.get(p).input[0]);
					var msgContainer = this._getRelativeContainer(el,this.container);

					for(i=0, l=errors[p].length; i<l; i++){
						content = (this.wrapper !== "") ? jQuery(this.wrapper).append(errors[p][i]) : errors[p][i];
						this.messages.push(content);
						jQuery(msgContainer).append(content);
					}
				}
			}
		} else {
			var errorCount = 0;
			for(var p in errors){
				if(errors[p].length > 0){
					var el = this.form.inputs.get(p).input[0];
					
					for(i=0, l=errors[p].length; i<l; i++){
						if(this.limit > 0 && errorCount == this.limit){
							break;
						}
						content = (this.wrapper !== "") ? jQuery(this.wrapper).append(errors[p][i]) : errors[p][i];
						this.messages.push(content);
						jQuery(this.container).append(content);
						errorCount++;
					}
				}
			}
		}
		if(this.errorElement.type == "relative"){
			for(var p in errors){
				if(errors[p].length > 0){
					var errorClassedEl = jQuery(this.form.inputs.get(p).input);
					var errorEl = this._getRelativeContainer(errorClassedEl,this.errorElement.element);
					this._addClass(errorEl);
				}
			}
		} else {
			this._addClass(this.errorElement.element);
		}
	},
	/**
	 * Removes the error messages from the DOM
	 */
	removeErrors:function(){
		for(var i=0, l=this.messages.length; i<l; i++){
			this.messages[i].remove();
		}
		for(var j=0, k=this.errorClassedElements.length; j<k; j++){
			this.errorClassedElements[j].removeClass(this.errorClass);
		}
		this.errorClassedElements.empty();
	},
	/**
	 * Returns the error messages DOM elements
	 * @returns An array containing all the error messages DOM elements
	 * @type Array
	 */
	getErrorElements:function(){
		return this.messages;
	},
	/**
	 * Gets the container element specified relative to an input
	 * @private
	 * @param {String} p			The name of the input
	 * @param {String} selector		A selector
	 * @returns A jQuery object representing the container
	 * @type jQuery
	 */
	_getRelativeContainer:function(el,selector){
		var container = jQuery(el);
		var matches = selector.match(this._funcRegExp);
		if(matches != null){
			for(var i=0, l=matches.length; i<l; i++){
				match = matches[i].substring(1,matches[i].length);
				args = matches[i].match(this._argsRegExp);
				args = args[0].replace(/[\'\"\(\)]/g, "");
				match = match.replace(this._argsRegExp,"");
				container = jQuery(container)[match](args);
			}
		}
		return container;
	},
	/**
	 * Adds the errorClass to the elements specified, absolute or relative
	 * @private
	 * @param {String} p 	The name of the input
	 */
	_addClass:function(el){
		/*var errorClassedEls;
		if(this.errorElement.type == "relative"){
			errorClassedEls = this._getRelativeContainer(el,this.errorElement.element);
		} else {
			errorClassedEls = this.errorElement.element;
		}
		this.errorClassedElements.push(jQuery(errorClassedEls));
		jQuery(errorClassedEls).addClass(this.errorClass);
		*/
		this.errorClassedElements.push(jQuery(el));
		jQuery(el).addClass(this.errorClass);
	}
});
