/**
 * @copyright Prifloat AB 
 */
(function($) {
	//
	// plugin definition
	//
	//
	$.pfAjax = function(url, options) {
		options = options || {};
		options.trigger = false;
		$(document.body).pfAjax(url, options);
	};
	$.pfAjax.getNextId = function() {
		return idCount+1;
	};
	
	$.fn.pfAjax = function(url, options) {
		
		// Default options
		var defaults = {
			data: '', // Additional post-data
			form: '',  // id of the form element.
			trigger: 'click', // the event(s) to listen to.  
			tinyMce: false, // Trigger save on tiny mce editors. 
			/*
			 *  Possible valuse for the spinners:
			 *  false, true, selector
			 */
			spinner: false,			// update (spinner appears inside. Previous content is lost.)
			spinnerDisable: false,	// disable + spinner appear on the right side
			spinnerReplace: false,	// replace (temporarly)
			spinnerOverlay: false,	// overlay a spinner in the center of the ajax selector
			/**
			 * spinnerText is either text or a selector. 
			 * If the selector finds fields, the spinnerText will be used as text. 
			 */
			spinnerText: '',
			spinnerIcon: '/imgs/animated/12_spinner_box.gif',
			
			/*
			 *  Callback for successful form submit
			 */
			success: null

		};
		
		// Extend our default options with those provided.
		// Note that the first arg to extend is an empty object -
		// this is to keep from overriding our "defaults" object.
		options = $.extend(defaults, options);
		
		
		/**
		 * Do the request. 
		 */
		function doRequest(request) {
			if (!request) {
				if(options.tinyMce) {
					if(options.tinyMce == true)
						tinyMCE.triggerSave();
				}				
				
				request = buildRequest.call(this);
				
				// We must show the spinner after build request, since spinnerDisable
				// may disable a form input, and therefor removing expected data from
				// the request. 
				_showSpinner($(this));
				
				
			}
			$.ajax({
				type: 'POST',
				url: request.url,
				data: request.data,
				dataType: 'json',
				error: function(transport, textStatus, errorThrown) {
					var msg = 'Unexpected server response: ' + transport.responseText;
					var messageContainer = $('#msg_'+options.form);
					if(messageContainer.length > 0) {
						messageContainer.html(msg).addClass('inputFieldError');
					} else {
						alert(msg);
					}
					_enableButton(request.initiator);
					_hideSpinner(request.initiator);
				},
				success: function(json) {		
					processResponse(json, request);
				} 
			});	
		}
		
		/**
		 * Build request
		 */
		function buildRequest() {
			var request = {
					id: ++idCount,
					initiator: $(this), 
					url: url, 
					data: options.data
				};
			 
			if(typeof request.data == 'function') {
				request.data = request.data.call(this);	
			}
			if(typeof request.data == 'object') {
				request.data = $.param(request.data);
			}
			
			// Tell the server that we want a json response. 
			request.data += (request.data ? '&' : '') + 'pfAjax=1';
			
			request.initiator.removeClass('inputFieldError').next('.fn-error-image').remove();
			
			// Form processing
			if(options.form) {
				request.data += '&'+$("#"+options.form).serialize();
				
				// Show loading	 
				$('#msg_'+options.form).addClass('msg-working').removeClass('msg-error msg-warning msg-success')
					.html($("#translate_working").html())
					.stop(true).css({opacity: 1}).show();
				
				if (request.initiator.hasClass('inputsubmit')) {
					
					// Disable button
					$('#bttn_'+options.form).add(request.initiator).attr('disabled','disabled').addClass('inputsubmitdisabled');
				}
				
				// Remove error messages
				$('#'+options.form+' .inputFieldError, #'+options.form+' .inputFieldWarning').removeClass('inputFieldError inputFieldWarning');
				// Remove error icons
				$('#'+options.form+' .fn-error-image').remove();
				
			}
			
			if (!request.url) {
				if (this.href) {
					request.url = this.href;
				} else if (options.form) {
					request.url = $('#'+options.form).attr('action');
				}
			}
			return request;
		}
		
		function processResponse(json, request) {
			var hasErrors = false,
				isConfirm = false;
				
			// Process response when eventual javascripts has finished loading. 
			function __processResponse() {
				$.each(json, function() {
					switch(this.type) {
						// In alphabetic order
						case 'addClass':
							$('#'+this.id).addClass(this.className);
							break;
						case 'alert':
							alert(this.message);
							break;
						case 'append':
							$('#'+this.id).append(this.data);
							if(this.highlight) {
								$('#'+this.highlight).effect("highlight", {}, 4000);
							}
							break;
						case 'attr':
							$('#'+this.id).css(this.attr, this.value);
							break;
						case 'confirm':
							if(confirm(this.message)) {
								this.name = this.name ? this.name : 'confirmed';												
								if (this.okUrl) {
									location.href = this.okUrl;
								} else {
									isConfirm = true;
									request.data += '&'+this.name+'=1';
									doRequest(request);
								}
							} else {
								if (this.cancelUrl) {
									location.href = this.cancelUrl;
								} else {
									_enableButton(request.initiator);
									$('#msg_'+options.form).hide(200, function() {
										$(this).removeClass(MSG_CLASS_ALL).html('');
									});
								}
							}
							break;
						case 'css':
							var exists = $('link[href='+this.href+']').length>0;
							if (!exists) {
								$('head').append('<link rel="stylesheet" type="text/css" media="'+this.media+'" href="'+this.href+'" />');
							}
							break;
						case 'error':
							if (options.form) {
								$('#msg_'+options.form).removeClass(MSG_CLASS_ALL).addClass('msg-alert').html(this.error);
								$('#'+options.form).show();
								_enableButton(request.initiator);
							}
							break;
						case 'errors':
							// Show form errors
							hasErrors = true;
							var errMsg = _processErrors(this.errors_header, this.errors, options);
							if (options.form) {
								if($('#msg_'+options.form).length > 0) {
									$('#msg_'+options.form).removeClass(MSG_CLASS_ALL).removeClass(MSG_CLASS_ALL).addClass('msg-error');
									$('#msg_'+options.form).html(errMsg).show();
								}
								_enableButton(request.initiator);
							} else {
								alert(errMsg);
							}
							break;
						case 'eval':
							window.eval(this.javascript);
							break;
						case 'event':
							$(document).trigger(jQuery.Event(this.event), this.data);
							break;
						case 'fb':
							$.facebox.settings.opacity = 0.5;
							$.facebox(this.html);
							break;
						case 'fbclose':
							// Only close if same content
							var content = $('#facebox').html();
							setTimeout(function() {
								//if($('#facebox').html() == content)
								$.facebox.close();
							}, this.wait);
							break;							
						case 'hide':
							$("#"+this.id).hide();
							break;
						case 'highlight':
							var id = this.id;
							function highlight() {
								$('#'+id).effect("highlight", {}, 3000);
							}
							if ($.effects.highlight !== undefined) {
								highlight();
							} else {
								$.getScript('/js/jquery/ui/effects.highlight.js', highlight);
							}
							break;
						case 'js':
							// Special case handled earlier
							break;
						case 'msg':
							_showMessage(this.msg, this.style);
							if (this.autoHide && options.form) {
								if (this.autoHide === true) {
									this.autoHide = 1000;
								}
								setTimeout(function() {
									$('#msg_'+options.form).fadeOut(1000);
								}, this.autoHide);	
							}
							break;
						case 'option':
							// Needs jquery.selectboxes.js
							var id = '#'+this.id;
							$(id).attr('disabled', false);
							$(id).children().remove();
							$.each(this.options, function(key,v) {
								$(id).addOption(key,v);
								// $('#'+this.id).append('<option id="'+key+'">'+v+'</option>');
							});
							$(id).selectOptions("0");
							break;
						case 'post':
							if (options.form) {
								$('#'+options.form).attr('action', this.url);
								$('#'+options.form).attr('method', 'post');
								$('#'+options.form).submit();
								$('#msg_'+options.form).hide();
							}
							else {
								alert('Error: No form to submit');
							}
							break;
						case 'prepend':
							$('#'+this.id).prepend(this.data);
							if(this.highlight) {
								$('#'+this.highlight).effect("highlight", {}, 4000);
							}
							break;
						case 'redirect':
							location.href=this.url;
							break;
						case 'reload':
							location.reload();
							break;
						case 'remove':
							runFxIfExists("#"+this.id, this.fx, {}, this.duration, function() {
								$(this).remove();
							});
						case 'removeClass':
							$('#'+this.id).removeClass(this.className);
							break;
						case 'replace':
							$("#"+this.id).replaceWith(this.data);
							if(this.highlight) {
								$('#'+this.highlight).effect("highlight", {}, 4000);
							}
							break;
						case 'show':
							$('#'+this.id).show();
							break;
						case 'sortorder':
							var table = this.table;
							var nr = 1;
							$.each(this.ids, function(i,n) {
								$('#'+table+'_'+n+'_nr').html('#'+nr);
								nr++;
							});
							break;							
						case 'update':
							$("#"+this.id).html(this.data);
							break;
						case 'updatemulti':
							var suffix = this.suffix;
							var prefix = this.prefix;
							$.each(this.values, function(i, val) {
								$("#"+prefix+i+suffix).html(val);
							});
							break;
						case 'value':
							$("#"+this.id).val(this.val);
							break;
						default:
							alert('unknown type : '+this.type);
					}
				});
				if (!isConfirm) {
					_hideSpinner(request.initiator);
					_enableButton(request.initiator);
					if (!hasErrors) {
						if (options.success) {
							options.success.call(request.initiator.get(0));
						}
						if ($('#msg_'+options.form).hasClass('msg-working')) {
							$('#msg_'+options.form).removeClass('msg-working').html('');
						}
						if (options.tinyMce) {
							if(options.tinyMce == 'update') {
								$('form textarea:hidden').each(function() {
									if($(this).next().hasClass('mceEditor')) {
										tinyMCE.getInstanceById($(this).attr('id')).getBody().innerHTML = $(this).val();
									}
								});
							}
						}
					}
				}
			}
			
			// Check for javascripts.  
			var scriptsToLoad = [];
			$.each(json, function() {
				if (this.type=='js') {
					var exists = $('script[src="'+this.src+'"]').length>0 || $.inArray(this.src, loadedScripts)>-1;
					if (!exists) {
						// if (this.async)
						scriptsToLoad.push(this.src);
					}
				}
			});
			// Load the javascripts in sync in case there are javascript dependencies between
			// the scripts to load. This could improve in the phuture by setting an async flag. 
			(function __loadScripts() {
				if (scriptsToLoad.length===0) {
					__processResponse();
				} else {
					var src = scriptsToLoad.shift();
					$.getScript(src, function() {
						loadedScripts.push(src);
						__loadScripts();
					});
				}
			})();
		}
		
		// Depending if we are binding the ajax request to an event, or simply performing it once, there are two paths to take. 
		if (options.trigger) {
			return this.each(function() {
				$(this).bind(options.trigger, function(ev) {
					ev.preventDefault();
					doRequest.call(this);
				});
			});
		} else {
			return this.each(doRequest);
		}

		
		// Process errors
		function _processErrors(header, errors, options)
		{
			var formErrors = '';
			var systemErrors = '';
			var newLine = options.form ? '<br/>' : '\n';
			header = header ? (options.form ? '<strong>'+header+'</strong>' : header) + newLine : '';
			// :TODO: If type = db_error then do something since an error have occured talking to the database
			$.each(errors, function() {
				switch(this.type) {
					case 'general_error':
						formErrors += '- '+this.err+newLine;
						break;
					case 'system_error':
						systemErrors += '- '+this.err+newLine;
						break;
					default:
						_showFieldError(this.id, this.err,  this.ico);
						if(this.err != "") {
							formErrors += '- '+this.err+newLine;
						}
				}
			});
			if(systemErrors != "") {
				return systemErrors;
			} else {
				return header+formErrors;
			}
		}
		
		function _showFieldError(id, message, showIcon)
		{
			var element = $("#"+id);
			if(element) {
				var img = element.next('.fn-error-image');
				if(!img.length) {
					element.after('<img class="fn-error-image" title="'+message.replace("\n", "")+'" style="display:none;margin-left: 10px;" src="/public/imgs/icons/12_alert.gif"/>');
					img = element.next('.fn-error-image');
				}
				
				if(element.type != 'file'){
					element.addClass('inputFieldError');
					element.removeClass('inputFieldWarning');
				}
				if(showIcon == 1){
					img.show();
				}
			}
		}
		function _showMessage(message, style) {
			var messageContainer = $('#msg_'+options.form);
			if (message) {
				if(messageContainer.length > 0) {
					if(style == 'ok')
						messageContainer.html(message).addClass('msg-success').removeClass('msg-working');
					else
						messageContainer.html(message).addClass('msg-error').removeClass('msg-working');
				} else {
					alert(message);
				}
			} else {
				messageContainer.hide().removeClass('msg-working');
			}
			if($('#bttn_'+options.form).length > 0) {
				$('#bttn_'+options.form).removeAttr("disabled").removeClass('inputsubmitdisabled');
			}
		}
		function _enableButton(obj) {
			$('#bttn_'+options.form).add(obj).removeAttr("disabled");
			$('#bttn_'+options.form).add(obj).removeClass('inputsubmitdisabled');
		}
		function _showSpinner(obj) {
			var spinnerText = options.spinnerText,
				style='';
			if (spinnerText) {
				spinnerText = $(document).find(options.spinnerText).html();
				if (!spinnerText) {
					spinnerText = options.spinnerText;
				}
				if (spinnerText) {
					style= ' style="vertical-align: middle;" ';
				}
			}
			
			var img = '<span class="bqr-ajax-spinner" style="white-space: nowrap;"><img  src="'+options.spinnerIcon+'" '+style+'/>'+spinnerText+'</span>';
			
			if (options.spinner) {
				var spinner = obj;
				if (options.spinner !== true) {
					spinner = $(options.spinner);
				}
				spinner.data('html', spinner.html());
				spinner.html(img);
			}
			if (options.spinnerDisable) {
				var spinnerDisable = obj;
				if (options.spinnerDisable !== true) {
					spinnerDisable = $(options.spinnerDisable);
				}
				spinnerDisable.attr('disabled', true);
				spinnerDisable.after(img);
			}
			if (options.spinnerReplace) {
				var spinnerReplace = obj;
				if (options.spinnerReplace !== true) {
					spinnerReplace = $(options.spinnerReplace);
				}
				spinnerReplace.hide();
				spinnerReplace.after(img);
			}
			if (options.spinnerOverlay) {
				var elem = obj;
				if (options.spinnerOverlay !== true) {
					elem = $(options.spinnerOverlay);
				}
				var pos = elem.position();
				elem.after('<div id="pfAjaxSpinnerOverlay"><div></div><table><tr><td style="width: 100%; height: 100%; text-align: center; vertial-align: middle;">'+img+'</td></tr></table></div>');
				var overlay = $('#pfAjaxSpinnerOverlay');
				overlay.children().css({position:'absolute', left: pos.left+'px', top: pos.top+'px', width: elem.width()+'px', height: elem.height()+'px'})
					.eq(0).css({backgroundColor:'#fff', opacity: 0.5});
			}
		}
		function _hideSpinner(obj) {
			var img='span.bqr-ajax-spinner';
			if (options.spinner) {
				var spinner = obj;
				if (options.spinner !== true) {
					spinner = $(options.spinner);
				}
				// Only remove and update if the spinner is still there. 
				if (spinner.children('span.bqr-ajax-spinner').length == 1) {
					spinner.html(spinner.data('html')).removeData('html');
				}
			}
			if (options.spinnerDisable) {
				var spinnerDisable = obj;
				if (options.spinnerDisable !== true) {
					spinnerDisable = $(options.spinnerDisable);
				}
				spinnerDisable.attr('disabled', false);
				spinnerDisable.nextAll(img).remove();
			}
			if (options.spinnerReplace) {
				var spinnerReplace = obj;
				if (options.spinnerReplace !== true) {
					spinnerReplace = $(options.spinnerReplace);
				}
				spinnerReplace.css({display: ''});
				spinnerReplace.nextAll(img).remove();
			}			
			if (options.spinnerOverlay) {
				$('#pfAjaxSpinnerOverlay').remove();
			}
		}
	};

	function runFxIfExists(selector, fx, fxOptions, fxDuration, callback) {
		var obj = $(selector);
		if (fx && $.fn[fx] != undefined) {
			obj[this.fx](fxDuration, callback);
		} else if (fx &&  $.effects[fx] != undefined) {
			obj.effect(fx, fxOptions, fxDuration, callback);
		} else {
			callback.call(obj.get(0));
		}
	}
	var MSG_CLASS_ALL = 'msg-error msg-success msg-warning msg-alert msg-working'
	var loadedScripts = [], 
		idCount=0;
//
// end of closure
//
})(jQuery);
