/**
 * 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.constructor = UIErrorField;
	this.constructor.name = "UIErrorField";
	
	if(form){
		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.messageContainer.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.errorClass = (opts && opts.errorClass) ? opts.errorClass : "error";
	this.wrapper = (opts && opts.wrapper) ? opts.wrapper : "<div>";
	this.messages = [];
	this.errorClassedElements = [];
	
	this._argsRegExp = /[\(][\'\"]?[\w]+[\'\"]?[\)]/;
	this._funcRegExp = /\.[\w]+[\(][\'\"]?[\w]+[\'\"]?[\)]/g;
}
UIErrorField.prototype = {
	/**
	 * 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 msgContainer = this._getRelativeContainer(p,this.container);
					this._addClass(p);

					for(i=0, l=errors[p].length; i<l; i++){
						content = jQuery(this.wrapper).append(errors[p][i]);
						this.messages.push(content);
						jQuery(msgContainer).append(content);
					}
				}
			}
		} else {
			for(var p in errors){
				if(errors[p].length > 0){
					this._addClass(p);

					for(i=0, l=errors[p].length; i<l; i++){
						content = jQuery(this.wrapper).append(errors[p][i]);
						this.messages.push(content);
						jQuery(this.container).append(content);
					}
				}
			}
		}
	},
	/**
	 * 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);
		}
	},
	/**
	 * 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(p,selector){
		var container = jQuery(this.form.form).find('*[name='+p+']');
		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(p){
		var errorClassedEls;
		if(this.errorElement.type == "relative"){
			errorClassedEls = this._getRelativeContainer(p,this.errorElement.element);
		} else {
			errorClassedEls = this.errorElement;
		}
		this.errorClassedElements.push(jQuery(errorClassedEls));
		jQuery(errorClassedEls).addClass(this.errorClass);
	}
};
