/*
Global Date, Math, ipc, window

3-95 "adtech"()
Closure _clean, _extract_key_params, _extract_params, _find_key_index, _getAugmentedUrl, _index_for_key, _params_to_push_after_key, _push_params, adcall

9-12 "_clean"()
Outer _index_for_key, adcall

14-27 "_getAugmentedUrl"()
Variable combined_key_value, misc
Outer _index_for_key, _params_to_push_after_key, adcall
Global Date, Math, window

29-37 "_extract_params"(url)
Variable params
Outer adcall

39-49 "_extract_key_params"()
Variable _start_value, key, key_value
Outer _index_for_key, adcall

51-60 "_find_key_index"()
Variable i
Outer _index_for_key, adcall

62-76 "_push_params"(o)
Outer _params_to_push_after_key
Global window

79-86 "getAugmentedUrl"(url)
Variable u
Outer _clean, _extract_key_params, _extract_params, _find_key_index, _getAugmentedUrl

87-89 "pushParams"(o)
Outer _push_params

90-92 "displayParamsToAdd"()
Outer _params_to_push_after_key
*/

/*members adgroupid, adtech, base_url, call, displayParamsToAdd, fn, 
getAugmentedUrl, getTime, indexOf, join, key_params, length, name, 
params, push, pushParams, random, round, splice, split, substring
*/

/*global document, window */
/* jslint code, do not remove, should be stripped via the minifier  */
var ipc = ipc || {};

ipc.adtech = function () {
	/**
	* Stores informationabout the ad calls
	* has three attributes
	* base_url: which is url for the adtech call
	* params: is an array that contains all the parameters passed on the url
	* key_params: is the paramters which make up the key parameter
	* @property adcall
	* @type {Object} 
	* @access private
	*/
	var adcall				= {}, // base_url, params, key_params
	
	/**
	* This stores the positional information of the parameter 'key'
	* it defaults to -1
	* @property _index_for_key
	* @type {Integer} Stores the position information of the key parameter
	* @access private
	*/
	 _index_for_key          = -1,
	
	/**
	* Holds information about all the additional parameters
	* that it needs to pass to key parameter
	* @property _params_to_push_after_key
	* @type {Array} Stores all the additional parameter that need to be augmented
	* @access private
	*/
	_params_to_push_after_key  = [],

	/**
	* Holds information about all the additional parameters
	* that it needs to pass to key parameter
	* @property _params_to_push_before_key
	* @type {Array} Stores all the additional parameter that need to be augmented
	* @access private
	*/
	_params_to_push_before_key  = [],
	
	/**
	* holds initial objects (with callback functions) so they
	* can be sorted by order
	* @type {Array}
	*/
	_initial_objects = [],
	
	/**
	* url parameters with encoded values to append to the rest of the string
	* @type {String}
	*/  
	_url_nvp_params_encoded = '',
	
	/**
	* Cleans up the private variables
	* for the next set of url that needs
	* cleaning up
	* @method _clean
	* @access private
	* @return void
	*/
	_clean  = function () {
		adcall          = {};
		_index_for_key  = -1;
		_params_to_push_after_key = [];
		_params_to_push_before_key = [];
	},

	/**
	* Returns the augmented url. This relies 
	* on all the additional parameters being added
	* to the key parameter for the adtech call.
	* @method _getAugmentedUrl
	* @access private
	* @return {String}   String which is the updated url
	*/
	_getAugmentedUrl = function () {
		var combined_key_value = '';
		// sort the parameters in ascending order
		_sort_initial_objects();
		// call the callback functions and populate the _params_to_push_after_key array
		_call_init_functions();
		//create the nvp values
		_push_nvp_params();
		// concat current keys values + new params to push
		combined_key_value = [_params_to_push_before_key.join('+'), adcall.key_params.join('+'), _params_to_push_after_key.join('+')].join('+');
	
		// update the params stack with the new key value
		// also replace the starting and ending +'s
	
		adcall.params[_index_for_key] = 'key=' + combined_key_value.replace(/^\+/,'').replace(/\+$/,'');
	
		// add grp 
		if(!window.adgroupid) {
			window.adgroupid = Math.round(Math.random() * 1000);
		}
		// add misc
		var misc  = 'misc=' + new Date().getTime(),
		grp   = 'grp=' + window.adgroupid;
		return [adcall.base_url, adcall.params.join(';'), 'grp=' + window.adgroupid, misc].join(';')+';nvp='+_url_nvp_params_encoded;
	},
	
	/**
	* Extracts all the parameters stored on the
	* url. This is later used to re-combine the url
	* @method _extract_params
	* @access private
	* @return void
	*/
	_extract_params = function (url) {
		var params = [];
		url = url.replace(/;$/, '');
		params = url.split(';');
	
		if (0 !== params.length) {
			adcall.base_url = params[0];
			adcall.params  = params.splice(1, params.length -1 );
		}
	},
	
	/**
	* Extract parameters of the 'key' parameter
	* the paramters are grouped together using + 
	* this updates the parameter adcall.key_params with 
	* an array containing all the 'key' parameters
	* @access private
	* @method _extract_key_params
	* @return void
	*/
	_extract_key_params = function () {
		// get the value assigned to the 'key'
		var key, _start_value;
		key = adcall.params[_index_for_key];
		if (key) {
		key = key.replace(/^key=/, '');
		}
		// store key params if key was found
		if(key) {
			_start_value = key.indexOf('=', 0);
			if(-1 != _start_value) {
				_start_value+=1; // incrementing to get the next position
				var key_value    = key.substring(_start_value, key.length);
				// extract the params passed to key
				adcall.key_params = key_value.split('+');
			}
		} else {
			// if key param wasn't found
			// create an empty placeholder
			adcall.key_params = [];
			// also set the index of the key param as the highest value
			// so that key is again to the very end
			_index_for_key = adcall.params.length;
		}
	},
	
	/**
	* Finds the positional information about 
	* the key parameter and updates the private property
	* _index_for_key
	* @access private
	* @method _find_key_index
	* @return void
	*/
	_find_key_index = function () {
		if (adcall && adcall.params && (adcall.params.length > 0)) {
			for(var i=0;i<adcall.params.length;i+=1) {
				if ('key=' == adcall.params[i].substring(0,4)) {
					_index_for_key = i;
					break; 
				}
			}
		}
	},

	/**
	* Pushes parameters onto a stack that will
	* hold information of the global variables
	* etc that will need to be added to the key parameter
	* Also allows you to pass decorator functions that will
	* re-decorate the global variable before its stored on the stack
	*
	* <pre>
	*   ipc.adtech.pushParams({'name': 'rsinetsegs', 'fn': function(val) {
	*     // define what to do with the value
	*     // in case of audience science im going to limit the value
	*     // to 20 before it gets pushed into the parameter
	*     var l = (val.length > 10)?10:val.length,
	*         v = '';
	*     for(var i=0;i<l;i+=1) {
	*       v+='rsi=' + val[i] + '+';
	*     }
	*     return v;
	*   }});
	* </pre>
	* @access private
	* @method _push_params
	* @return void
	*/
	_push_params = function (o) {
		// if order is not defined, ignore the object
		if(o.order){
			// the property should first exist or
			// ignore silently
			if (o.name && window[o.name]) {
				_initial_objects.push(o);
			}
		} else {
			_log_error('undefined propery "order" for one of your objects');
		}
	},
	
	/**
	* call the callback functions and populate the _params_to_push_after_key
	* @access private
	* @method _call_init_functions
	* @return void
	*/
	_call_init_functions = function (){ 
		var holder;
		for(var i=0;i<_initial_objects.length;i+=1){
			// if decorator function is present
			// then decorate it and then push that information
			// inside the holder
			holder = _params_to_push_after_key;
			if (_initial_objects[i].placement == 'before') {
			 holder = _params_to_push_before_key;
		  }
			if (_initial_objects[i].fn  && (typeof _initial_objects[i].fn == 'function')) {
				holder.push(_initial_objects[i].fn.call(_initial_objects[i].fn, window[_initial_objects[i].name]));
				// else just save it a regular name value pair
			} else {
				holder.push(_initial_objects[i].name + '=' + window[_initial_objects[i].name]);
			}
		}
	},
	/**
	* Sort the objects based on their order property in descending order
	* equal order numbers will follow the ordering of pushing the object
	* @access private
	* @method _sort_initial_objects
	* @return void
	*/
	_sort_initial_objects = function (){
		_initial_objects.sort(function (a,b){
			return a.order - b.order;
		});
	},
	/**
	* Create the urlencoded nvp values
	* @access private
	* @method _push_nvp_params
	* @return void
	*/
	_push_nvp_params = function (o){
		for(var prop in o){
			if(!prop.hasOwnProperty(o)){continue;}
			if(typeof o[prop] == 'number' || typeof o[prop] == 'string'){
				_url_nvp_params_encoded += '&kv'+prop+'='+escape(o[prop]);
			}   		
		}
	},
	_log_error = function (message){
		message = 'Error: '+message;
		if(console && console.log) {
			console.log(message);
		} else {
			alert(message);
		}
	};
	
	return {
		/**
		* Takes the adtech url and disects it
		* updates the 'key' parameter and adds
		* additional parameters that have been pushed onto it
		* and then return back the augmented url
		* @access public
		* @method ipc.adtech.getAugmentedUrl
		* @static
		* @return {String} Returns the updated url with additional parameter
		*/
		getAugmentedUrl: function (url) {
			_extract_params(url);
			_find_key_index();
			_extract_key_params();
			var u = _getAugmentedUrl();
			_clean();
			return u;
		},
		
		/**
		* Push parameter to the stack. These parameters are 
		* picked up from the global namespace and pushed on to the array
		* which are later used to augment the url 
		* @access public
		* @method ipc.adtech.pushParams
		* @static
		* @param {Object} can take to attributes 'name' [required], 'fn': [function signature that can be called]
		* @return void
		*/
		pushParams: function (o) {
			_push_params(o);
		},
		
		/**
		* Push an Object to the stack
		* @access public
		* @method ipc.adtech.pushNvp
		* @static
		* @param {Object} can take any attributes as long as the value is a string
		* @return void
		*/
		pushNvp: function (o) {
			_push_nvp_params(o);
		},
		
		/**
		* Displays all the parameter that have already been pushed
		* Very useful for debugging to test if the decorator function passed
		* worked as expected.
		* @access public 
		* @method ipc.adtech.displayParamsToAdd
		* @static
		* @return void
		*/
		displayParamsToAdd: function () {
			return _params_to_push_after_key;
		}
	};
}();
