/**
 * simple jquery extension to handle errors
 */
if(jQuery) (function($) {
	$.formHandler = function(err, data) {
        err = (err == "") ? {} : err;
        data = (data == "") ? {} : data;

        // populate the form with existing data
		var formid = (data.htmlformid != undefined) ? '#' + data.htmlformid : 'form';
        $(formid).populate(data);

        // append errors to the current form
        if (err.htmlformid != undefined) {
            var formid = err.htmlformid;

            for (var name in err) {
                var message = err[name];
                var selector = '#' + formid + ' *[@id=' + name +']';
				$(selector).addClass('error');
				$(selector).parent().append('<span class="error"><span>' + message + '</span></span>');
            }
        }

    }

})(jQuery);

jQuery.fn.populate = function(obj, options) {


	// ------------------------------------------------------------------------------------------
	// JSON conversion function

	// convert
	function parseJSON(obj, path) {
		// prepare
		path = path || '';

		// iteration (objects / arrays)
		if(obj == undefined) {

		} else if(obj.constructor == Object) {
			for(var prop in obj) {
				var name	= path + (path == '' ? prop : '[' +prop+ ']');
				parseJSON(obj[prop], name);
			}
		} else if(obj.constructor == Array) {
			for(var i = 0; i < obj.length; i++) {
				var index	= options.useIndices ? i : '';
				index		= options.phpNaming ? '[' +index+']' : index;
				var name	= path + index;
				parseJSON(obj[i], name);
			}
		} else { // assignment (values)
			// if the element name hasn't yet been defined, create it as a single value
			if(arr[path] == undefined) {
				arr[path] = obj;
			} else if(arr[path].constructor != Array) {
				// if the element name HAS been defined, but it's a single value, convert to an array and add the new value
				arr[path] = [arr[path], obj];
			} else {
				// if the element name HAS been defined, and is already an array, push the single value on the end of the stack
				arr[path].push(obj);
			}
		}
	};


	// ------------------------------------------------------------------------------------------
	// population functions
	function debug(str) { if(window.console && console.log) { console.log(str); } }
	function getElementName(name) { return (!options.phpNaming) ? name.replace(/\[\]$/,'') : name; }

	function populateElement(parentElement, name, value) {
		var selector	= options.identifier == 'id' ? '#' + name : '[' +options.identifier+ '="' +name+ '"]';
		var element		= jQuery(selector, parentElement);
		value			= value.toString();
		value			= value == 'null' ? '' : value;
		element.html(value);
	}

	function populateFormElement(form, name, value) {
		// check that the named element exists in the form
		var name = getElementName(name); // handle non-php naming
		var element	= form[name];
		if(element == undefined) {
			debug('No such element as ' + name);
			return false;
		}

		// debug options
		if(options.debug) {
			_populate.elements.push(element);
		}

		// now, place any single elements in an array.
		// this is so that the next bit of code (a loop) can treat them the
		// same as any array-elements passed, ie radiobutton or checkox arrays,
		// and the code will just work
		elements = element.type == undefined && element.length ? element : [element];

		// populate the element correctly
		for(var e = 0; e < elements.length; e++) {
			var element = elements[e];

			switch(element.type || element.tagName) {

				case 'radio':
					// use the single value to check the radio button
					element.checked = (element.value != '' && value.toString().toLowerCase() == element.value.toLowerCase());

				case 'checkbox':
					// depends on the value.
					// if it's an array, perform a sub loop
					// if it's a value, just do the check

					var values = value.constructor == Array ? value : [value];
					for(var j = 0; j < values.length; j++) {
						element.checked |= element.value == values[j];
					}

					//element.checked = (element.value != '' && value.toString().toLowerCase() == element.value.toLowerCase());
					break;

				case 'select-multiple':
					var values = value.constructor == Array ? value : [value];
					for(var i = 0; i < element.options.length; i++) {
						for(var j = 0; j < values.length; j++) {
							element.options[i].selected |= element.options[i].value == values[j];
						}
					}
					break;

				case 'select':
				case 'select-one':
					element.value	= value.toString() || value;
					break;

				case 'file':
					// do not attempt to set the value of file elements
					break;

				case 'text':
				case 'button':
				case 'textarea':
				case 'submit':
				default:
					value			= value == null ? '' : value;
					element.value	= value;
			}

		}

	}



	// ---------------
	// options & setup

	// exit if no data object supplied
	if (obj === undefined) { return this; };

	// options
	var options = jQuery.extend(
								{
								phpNaming:		true,
								phpIndices:		false,
								resetForm:		true,
								identifier:		'id',
								debug:			false
								},
								options
								);

	if(options.phpIndices) {
		options.phpNaming = true;
	}

	// convert hierarchical JSON to flat array
	var arr	= [];
	parseJSON(obj);

	if(options.debug) {
		_populate = {
			arr:		arr,
			obj:		obj,
			elements:	[]
		}
	}

	// main process function
	this.each(function() {
		// variables
		var tagName	= this.tagName.toLowerCase();
		var method	= tagName == 'form' ? populateFormElement : populateElement;

		// reset form?
		if(tagName == 'form' && options.resetForm) {
			this.reset();
		}

		// update elements
		for(var i in arr) {
			method(this, i, arr[i]);
		}
	});

	return this;
};