/**
 * @author Greg Pasquariello
 * 
 * Creates an instance of the form manager.  The form manager uses existing HTML form 
 * markup and manages binding of form fields to backend data services.
 * 
 * In order for this mechanism to work, the form fields must have a "name" attribute
 * that matches what the web services send and receive as data.  So, for example, if the 
 * load service sends a value for "long_description", and the save service accepts
 * a value called "long_description", any fields named "long_description" will be
 * properly bound to the service data.  Note that the field must be named using the "name"
 * attribute, not the "id" attribute.
 * 
 * The form manager reads data and populates all form field types as well as <DIV> or <SPAN>
 * tags.  It will post all form field types except <DIV> or <SPAN> tags.
 * 
 * If you wish to populate additional elements, like Ext component types or TinyMCE for example,
 * simply add an onFormLoadComplete handler to deal with your particular situation.
 * 
 * This class requires ext-core.js from the Ext library.
 * 
 * The config parameter accepts the following:
 * 
 * loadUrl:					The URL to call to load the form data.
 * submitUrl:				The URL to call to save the form data.
 * onFormValidationFailure:	Called when the form validation fails.  If not set
 * 							the default is used.
 * onFormSubmitComplete:	Called when the form submit completes.  Sends formData.
 * onFormLoadComplete: 		Called when the form load completes and the page is
 * 							populated.  formData is passed in.
 * onFormLoadFailure:		Called if the form load failed.  formData is passed in.
 * spinnerName:				The name of the DOM element to display as the "ajax_wait" 
 * 							spinner.  Defaults to "ajax_wait"
 * 							
 * @param {Object} config
 */
Ext.namespace("P5");

P5.FormManager = function(config) {
	Ext.apply(this, config);
	
	if (!this.spinnerName) {
		this.spinnerName = "ajax_wait";
	}
	
	Ext.Ajax.on('beforerequest', this.showSpinner, this);
	Ext.Ajax.on('requestcomplete', this.hideSpinner, this);
}



/**
 * Submit the form to the server.
 * 
 * @param {Object} params
 */
P5.FormManager.prototype.submit = function(params, onFormSubmitComplete) {
	if (!this.submitUrl) {
		throw "This form manager does not have a URL for submission."
	}
	
	if (!params) {
		params = new Object();
	}
	
	params.op = params.op || "save";
	
	this.clearErrors();
	
	Ext.Ajax.request({
		url: this.submitUrl,
		method: 'POST',
		form: this.form,
		success: function(response, options){
			try {
				//
				// When the form is uploaded via ajax, data comes back normally in the response.responseText
				// unless the form enctype is 'multipart/form-data'.  In this case, it come back in an iframe on 
				// the response.responseXML.
				//
				// Below, we get the message depending on the form enctype. And of course, IE holds the data
				// differently. :(
				//
				// -gpasq
				//
				var enctype = "none";
				
				if (document.forms[options.form].attributes["enctype"]) {
					enctype = document.forms[options.form].attributes["enctype"].nodeValue;
				}
				
				var resultData;
				
				switch(enctype) {
					case "multipart/form-data":
						if (Ext.isIE) {
							resultData = eval("((" + response.responseXML.firstChild.innerText + "))");
						}
						else {
							resultData = eval("((" + response.responseXML.body.textContent + "))");
						}
						break;
					default:
						resultData = eval("((" + response.responseText + "))");
						break;
				}
				
				if (resultData.success == false) {
					if (this.onFormValidationFailure) {
						this.onFormValidationFailure(resultData);
					}
					else {
						this.handleFormValidationFailure(resultData);
					}
				}
				else {	
					onFormSubmitComplete = onFormSubmitComplete || this.onFormSubmitComplete;
					
					if (onFormSubmitComplete) {
						try {
							onFormSubmitComplete(resultData);
						} catch(err){}
					}
				}
			} 
			catch (err) {
				throw "Syntax Error - Bad data returned to submitForm from posting service.  Stack was: " + err.stack;
			}
		},
		scope: this,
		params: params
	});
}



/**
 * Loads the data from the loadUrl address and calls populateForm
 * to bind it to the form.
 * 
 * If you wish to populate the from differently, you can just override
 * the populateForm method.
 * 
 * @param {Object} params
 */
P5.FormManager.prototype.load = function(params) {
	if (!params) {
		params = new Object();
	}
	
	params.op = "load";
	
	Ext.Ajax.request({
		url: this.loadUrl,
		success: function(response) {
			if (response.status == 200) {
				formData = eval("((" + response.responseText + "))");
				if (formData.success == true) {
					this.populateForm(formData);
					if (this.onFormLoadComplete) {
						this.onFormLoadComplete(formData);
					}
				}
				else {
					if (this.onFormLoadFailure) {
						this.onFormLoadFailure(formData);
					}
				}
			}
		},
		scope: this,
		params: params
	});
}

/**
 * This function works with the specified lookup service to perform
 * generic lookups for such things as populating dropdowns, etc.
 * 
 * @param {Object} params
 * @param {Object} onLookupSuccess
 */
P5.FormManager.prototype.lookup = function(params, onLookupSuccess) {
	Ext.Ajax.request({
		url: '/services/lookup.php',
		success: function(response) {
			if (response.status == 200) {
				var obj = eval("((" + response.responseText + "))");
				if (onLookupSuccess) {
					onLookupSuccess(obj);
				}
			}
		},
		params: params
	});
}

/**
 * Returns the named url parameter, if one exists.  This is just a shortcut
 * to getUrlParameter();
 * 
 * @param {Object} name
 */
P5.FormManager.prototype.gup = function(name) { return this.getUrlParameter(name); }

/**
 * Returns the named url parameter, if one exists.
 * 
 * @param {Object} name
 */
P5.FormManager.prototype.getUrlParameter = function(name){
	return P5.FormManager.getUrlParameter(name);
}

P5.FormManager.getUrlParameter = function(name) {
	name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
	var regexS = "[\\?&]" + name + "=([^&#]*)";
	var regex = new RegExp(regexS);
	var results = regex.exec(window.location.href);
	if (results == null) {
		return "";
	}
	else {
		return results[1];
	}
}

/**
 * Create and show the spinner while the server is processing.
 */
P5.FormManager.prototype.showSpinner = function() {
	elems = Ext.DomQuery.select("*[name=ajax_wait]");
	for (var i=0; i < elems.length; i++) {
		elems[i].style.visibility = "visible";
	}
}

P5.FormManager.prototype.showSpinner2 = function() {
	elems = Ext.DomQuery.select("*[name=ajax_wait]");
	for (var i=0; i < elems.length; i++) {
		elems[i].style.display = "block";
	}
}

/**
 * Hide the spinner when processing is done.
 */
P5.FormManager.prototype.hideSpinner = function() {
	elems = Ext.DomQuery.select("*[name=ajax_wait]");
	for (var i=0; i < elems.length; i++) {
		elems[i].style.visibility = "hidden";
	}
}

P5.FormManager.prototype.hideSpinner2 = function() {
	elems = Ext.DomQuery.select("*[name=ajax_wait]");
	for (var i=0; i < elems.length; i++) {
		elems[i].style.display = "none";
	}
}

/**
 * Gets the value of the named form element.
 * 
 * @param {Object} elementName
 */
P5.FormManager.prototype.getFieldValue = function(elementName)
{
	var value = null;
	
	var fields = this.findFields(elementName);
	
	for(var i=0; i < fields.length; i++) {
		var field = fields[i];
	
		switch(field.type) {
			case "checkbox":
				if (field.checked) {
					value = field.value;
				}
				break;
			case "text":
			case "textarea":
				value = field.value;
				break;
			case "password":
				value = field.value;
				break;	
			case "radio":
				if(field.checked) {
					value = field.value;
				}
				break;
			case "select-one":
				if (field.selectedIndex >= 0) {
					value = field.options[field.selectedIndex].value;
				}
				break;
			case "undefined":
			default:
				break;
		}
	}
	return value;
}

/**
 * Set the values of the specific fields.  Works with all form types, <DIV>s and <SPAN>s.
 * 
 * @param {Object} elem
 * @param {Object} value
 */
P5.FormManager.prototype.setFieldValue = function(elementName, value) {
	var fields = this.findFields(elementName);
	
	for(var i=0; i < fields.length; i++) {
		var field = fields[i];
		
		switch (field.type) {
			case "text":
			case "textarea":
				field.value = value;
				break;
			case "password":
				field.value = value;
				break;
			case "checkbox":
				if (field.value == 'on') {
					value = value == null ? null : value.toLowerCase();
					
					if (value == "1" || value == "t" || value == "true") {
						field.checked = true;
					}
					else {
						field.checked = false;
					}
				}
				else {
					if (field.value == value) {
						field.checked = true;
					}
				}	
				break;
			case "select-one":
				for (var i=0; i < field.options.length; i++) {
					var option = field.options[i];
					if (option.value == value) {
						option.selected = true;
					}
					else {
						option.selected = false;
					}
				}
				break;
			case "radio":
				if (field.value == value) {
					field.checked = true;
				}
				break;
			default:
				if (field.tagName == "DIV" || field.tagName == "SPAN" || field.tagName == "TD") {
					field.innerHTML = value;
				}
				break;
		}
	}
}


/**
 * This is the default error handler.
 * 
 * If the form validation fails, iterate through the errors.  For every
 * error, look for an element of the same name with "_error" appended to it
 * and set it's value to the specified error message.
 * 
 * @param {Object} resultData
 */
P5.FormManager.prototype.handleFormValidationFailure = function(resultData) {
	for(key in resultData.errors) {
		this.setFieldValue(key + "_error", resultData.errors[key]);
	}
}

/**
 * Empty out the error fields.
 */
P5.FormManager.prototype.clearErrors = function() {
	var elems = Ext.DomQuery.select("*[name$=_error]");
	for (var i=0; i < elems.length; i++) {
		var errorField = elems[i];
		elems[i].innerHTML = "";
	}
}

P5.FormManager.prototype.findFields = function(name) {
	return Ext.DomQuery.select("*[name=" + name + "]");
}

/**
 * Bind the loaded data to the form.  First, we look for all HTML input
 * fields that map to the data key names and set those values.  Then
 * we scan for Ext components that match the field names.
 * 
 * @param {Object} formData
 */
P5.FormManager.prototype.populateForm = function(formData){
	for (key in formData.data) {
		this.setFieldValue(key, formData.data[key]);
	}
}
