(function($) {

Cobalt = (Cobalt === undefined) ? {} : Cobalt; 
Cobalt.Website = (Cobalt.Website === undefined) ? {} : Cobalt.Website;
Cobalt.Website.Common = (Cobalt.Website.Common === undefined) ? {} : Cobalt.Website.Common;

var cache = {};

var defaults = {
     target        : 'body'
   , loadingMarkup : '<span class="cobalt-AjaxCell-loading">Loading...</span>'
   , prefetch      : []
   , callback      : function() {}
   , maxTries      : 3
   , parallel      : false
};

/**
 * @requires {Cobalt.Core.JQueryFactory}
 *
 * Provides a mechanism for fetching image-cell contents by name.
 * @constructor
 * @param {array} options - list of options key pair values:
 *                 	prefetch: {array} list value of cellNames to fetch now (period)
 *                 	loadingMarkup: {string} html that will be in all "apply-to" divs until content is loaded.
 */
Cobalt.Website.Common.AjaxCell = function(options) {
	this.options = $.extend({}, defaults, options);
	this.cacheFullyLoaded = false;
	if(this.options.prefetch.length){
		this.prefetch(this.options.prefetch, this.options.parallel);		
	}
}


/*================================================================== 
				  UTILITY (Private) FUNCTIONS
	These are a collection of functions that should not be accessed
	publically.
  ------------------------------------------------------------------*/
  
/**
 * {PRIVATE} FETCH 
 *   Load content for the provided cellName.
 *   this is essentially a utility function that is responsible for steps that would otherwise be strewn across several other functions.
 *
 * @param {String} cellName The name of the cell being requested.
 */
Cobalt.Website.Common.AjaxCell.prototype.priv_fetch = function(cellName) {
  var cellName = this.formatCellName(cellName);
  var request = this.createRequest(cellName, false);

  // If empty, make the request
  this.priv_launchAjaxRequest(request, cellName);
  
  
};



/**
 * {PRIVATE} launchAjaxRequest
 *
 *   Do the actaual AJAX call for this cell content, this function is responsible for
 *   binding this object to the request completion callback.
 *
 * @param {String}  request url
 * @param {String}  cellName requested
 * @param {int} 	which attempt are we on, used as pass-through.
 */
Cobalt.Website.Common.AjaxCell.prototype.priv_launchAjaxRequest = function(request, cellName, attempt) {
	var THIS = this;
	if(attempt==undefined) {
		var attempt = 1;
	}
	$.ajax({
		  url      : request
		, dataType : 'html'
		, cache    : false
		, type     : 'get'
		, complete : function(xhr, textStatus){
			THIS.priv_fetchComplete.apply(THIS, [xhr, textStatus, cellName, attempt, request]);
		}
	});
}

/**
 * {PRIVATE} fetchComplete
 *
 *   Actions performed when the ajax call has completed.
 *     WHEN this.options.parallel is FALSE, this will attempt to fecth the
 *       contents until either 
 *         A. SUCCESS:  Successful and callback is called, next request is processed.
 * 
 *         B. FAILS:    Max retry atempts have been met, at wich
 *						time the error state callback will be fired.
 *						Then NEXT Request will be processed.
 *
 * @param {String}  {string} response from server
 * @param {String}  {string} status
 * @param {String}  {string} xhr 
 * @param {String}  cellName requested
 * @param {int} 	which attempt are we on, compared to maxTries
 * @param {String}  request url
 */
Cobalt.Website.Common.AjaxCell.prototype.priv_fetchComplete = function(response, status, cellName, attempts, request) {
	var fetchNextItem= true;
	var THIS = this;
	switch(status){
		case "success":
			cache[cellName].response.value  = response.responseText;
			cache[cellName].response.status = status;
			
			cache[cellName].loadStatus      = -1; // Flag as this cell is being updated
			this.priv_applyServerResponseToDomList(cellName);
			this.priv_callCallback(cellName, status);
			cache[cellName].loadStatus      = 1;  // Flag this cell as fully-loaded
			break;
			
		default:
			if(attempts <= this.options.maxTries){
				// relaunch request without firing callback (yet)
				this.priv_launchAjaxRequest(request, cellName, attempts+1);
				runNext = false;
				break;
			}else{
				// Perform callback for this status, as it is now final!
				this.priv_callCallback(cellName, status);
			}
	}
	
	if(!this.options.parallel && fetchNextItem){
		if(this.fetch_queue.length){
			this.requestCellContent(this.options.prefetch[this.fetch_queue.shift()]);
		}else{
			this.options.callback();
			
		}
	}else if(this.options.parallel && this.cacheFullyLoaded){
		
	}
}

/**
 * {PRIVATE} priv_applyToList
 *
 *   Apply serverResponse of this cell to the associated DOM elements.
 *
 */
 Cobalt.Website.Common.AjaxCell.prototype.priv_applyServerResponseToDomList = function(cellName){
	if(cache[cellName].applyTo.length){
		for(var i = 0; i < cache[cellName].applyTo.length; i++){
			cache[cellName].applyTo[i].html(cache[cellName].response.value);
			this.priv_callCallback(cellName, "success");
		}
	}
}

/**
 * {PRIVATE} callCallback
 * Will perform callback if callback function exists on for given cell and status
 *
 * @param {string} cellName, used as key against cache.
 * @param {string} run callback of this status.
 */
Cobalt.Website.Common.AjaxCell.prototype.priv_callCallback = function(cellName, status) {
	if(
		typeof(cache[cellName].callbacks)!="undefined"
		&& typeof(cache[cellName].callbacks[status])!="undefined"
		&& typeof(cache[cellName].callbacks[status])=="function"){
		cache[cellName].callbacks[status]();
	}
}


/**
 * Creates a request using the provided cellName.
 *
 * @param {String} cellname The name of the cell being requested.
 */
Cobalt.Website.Common.AjaxCell.prototype.createRequest = function(cellName, formatCellName) {
  var result = 'ajaxCell.ajax?',
      formatCellName = arguments[1] || true,
      cellName = (formatCellName) ? this.formatCellName(cellName) : cellName,
      parts = [];
  if (editMode === true) {
    parts.push('webId=' + ContextManager.getWebId());
    parts.push('locale=' + ContextManager.getLocale());
    parts.push('contextPath=/wsm');
    parts.push('editMode=true');
  }

  parts.push('version=' + ContextManager.getVersion());
  parts.push('pageName=' + ContextManager.getPageName());
  parts.push('cellName=' + cellName);
  parts.push(new Date().getTime() + '=cachebuster');
  return result + parts.join('&');
};


/**
 * Formats any string to a string that can be mapped to an image-cell.
 *
 * @param {String} str The string being formatted.
 */
Cobalt.Website.Common.AjaxCell.prototype.formatCellName = function(str) {
  str = str.replace(/^[\s]*|[\s]*$/ig,'') // trim leading/trailing whitespace...
           .replace(/<[\/a-z]*>/ig,'')  // tags...
           .replace(/[\s]/ig, '_')      // spaces...
           .replace(/[^a-z0-9_]/ig,''); // and non alpha-numeric chars
  return str;
};





/*================================================================== 
							OBJECT METHODS
  ------------------------------------------------------------------
	Below are functions that comprise the API for this object,
	excluding the constructor, found above.
  ------------------------------------------------------------------*/

  
/**
 * Prefetch the contents of these cellNames.
 * @param {Array} list value of cellNames to fetch now (period)
 */
Cobalt.Website.Common.AjaxCell.prototype.prefetch = function(arr_cellNames, fetchInParallel) {
	this.options.parallel = fetchInParallel;
	if(this.options.parallel){
		for(var i = 0; i< arr_cellNames.length; i++){
			this.requestCellContent(arr_cellNames[i]);
		}	
	}else{
		this.fetch_queue = [];
		for(var key in this.options.prefetch){
			this.fetch_queue.push(key);
		}
		this.requestCellContent(arr_cellNames[this.fetch_queue.shift()]);
	}
}



/**
 * If the content of cellName has not already been requested, request it, adding callback to the request
 *
 * @param {String} cellName, used as key against cache.
 * @param {Array}  list of callbacks to run for given status
 */
Cobalt.Website.Common.AjaxCell.prototype.requestCellContent = function(cellName, callbacks) {

	// Short Circuit if already configured.
	if(cache.hasOwnProperty(cellName))
		return; 
		
	if(typeof("callbacks")=="undefined")
		callbacks= {};
		
	cache[cellName] = {};
		
	cache[cellName].applyTo		= []; // jQuery Dom Object to apply this data to when it comes in.
	cache[cellName].url			= "";
	cache[cellName].response	= {value:"", status:""}; // response value
	cache[cellName].callbacks	= callbacks;
	cache[cellName].loadStatus	= 0; // -1: ACTIVELY processing response
									 //  0: not yet loaded
									 //  1: Completed, though there could be an error (see response.status)	
	this.priv_fetch(cellName);
}


/**
 * Once this content is loaded, apply to this container with these sets of callbacks:
 *   Where callbacks is an key-value object  strStatus : fnxToQueue
 *		All fnxToQueue should be functions to be performed given the status
 *      status may have values of:
 *          success
 *          notmodified
 *          error
 *          timeout
 *          abort
 *          parsererror
 *          
 */
Cobalt.Website.Common.AjaxCell.prototype.applyTo = function(jqDomEl_container, cellName, callbacks) {
	// Short-Circuit: 
	// If this cell name has not been configured, we can't apply it to anything.
	if(!cache.hasOwnProperty(cellName))	
		this.requestCellContent(cellName, callbacks);
	
	// add the callbacks to this cell
	cache[cellName].callbacks = callbacks;

	
	// If loading status is not "active" (not loaded yet) then just add this dom element to the apply-to list.
	if(!cache[cellName].loadStatus){
		cache[cellName].applyTo.push(jqDomEl_container);
		jqDomEl_container.html(this.options.loadingMarkup);

	// Otherwise just update dom object with value.
	}else if(cache[cellName].response.status=="success"){
		jqDomEl_container.html(cache[cellName].response.value)
		this.priv_callCallback(cellName, "success");
	}
}

Cobalt.Website.Common.AjaxCell.prototype.log = function(msg) {
	if(typeof(console)=="undefined"){
		console = {log:function(){}};
	}
	console.log(msg);
}

}(Cobalt.Core.JQueryFactory.getLatest()));

