mam.config = new function() {
	// Public Methods - this is the API (these are infact Priviledged Methods as they have access to private members)
	jQuery.extend(this,  { // use the jQuery extend function to add methods to the current object (this)
		version: "1.0",
		getControlXml: function() {
			return controlXml;
		},
		getSiteConfigXml: function() {
			return siteConfigXml;
		},
		getSiteSelectorXml: function() {
			return siteSelectorXml;
		},
		initialize: function(callback, parms) {
			if(!parms) {
				parms = {
					controlXml: true,
					siteConfigXml: true,
					siteSelectorXml: false
				}
			}
			if(controlXml == undefined && parms.controlXml) initControlXml();
			if(siteConfigXml == undefined && parms.siteConfigXml) initSiteConfigXml();
			if(siteSelectorXml == undefined && parms.siteSelectorXml) initSiteSelectorXml();
			queue[queue.length] = callback;
			if(activeLoads == 0) {
				process();
			}
		},
		setControlXmlPath: function(path) {
			controlXmlPath = path;
		},
		setSiteSelectorXmlPath: function(path) {
			siteSelectorPath = path;
		}
	});
	var controlXml = undefined; // internal variable to hold the control xml
	var siteConfigXml = undefined; // internal variable to hold the site config xml
	var siteSelectorXml = undefined; // internal variable to hold the site selector xml
	
	var controlXmlPath = "/control/control.xml"; // path to load the control xml from
							// Note: site config xml path is determined held in the control xml
	var siteSelectorPath = "/control/site-selector.xml"; // path to load the site selector xml from
	
	var queue = []; // queue of functions to be executed on completion of initialisation
	var queues = []; // array of queues for syncronizing dependent config
	var blocking = []; // array to hold the blocking state of the above queues
	var loading = {controlXml: false, siteConfigXml: false}; // array to hold the loading state of a particular config. Used when there is a dependency. 
				// A dependent canfig loader can check whether the config it depends on is currently being loaded before it decides to load it itself
	var timeout = []; // array of timeouts
	var activeLoads = 0; // variable to hold the numbver of loads currently active. Subsequent processing must wait until activeLoads = 0
	var loadID = 0; // Load ID assigned to each load. Used to identify metadata relating to the load such as its timeout process
	var queueIds = ["default", "controlXml"]; // registered syncronization queues
	for(var i=0; i<queueIds.length; i++) {
		queues[queueIds[i]] = [];
	}
	var asyncTimeout = 10; // seconds for async timeout
	var debug = false;
	
	// Private Methods
	var initControlXml = function() {
		loading["controlXml"] = true;
		var loadID = registerLoad();
		synchronize(function() {
			stop("controlXml");
			
			$.get(controlXmlPath,function(xml) {
				controlXml = xml;
				loading["controlXml"] = false;
				start("controlXml");
				loadComplete(loadID);
			});
		}, "controlXml");
	};
	var initSiteConfigXml = function() {
		if(controlXml == undefined && loading["controlXml"] == false) initControlXml();
		if(siteConfigXml == undefined && loading["siteConfigXml"] == false) { // do not load if alredy queued or loading
			loading["siteConfigXml"] = true;
			var loadID = registerLoad();
			synchronize(function() {
				stop("controlXml");
				$.get(mam.util.path.getSiteConfigPath(),function(xml) {
					siteConfigXml = xml;
					loading["siteConfigXml"] = false;
					start("controlXml");
					loadComplete(loadID);
				});
			}, "controlXml");
		}
	};
	var initSiteSelectorXml = function() {
		var loadID = registerLoad();
		$.get(siteSelectorPath,function(xml) {
			siteSelectorXml = xml;
			loadComplete(loadID);
		});
	};
	var synchronize = function(callback, id) {
		if(!id) id = "default";
		queues[id][queues[id].length] = callback;
		printDebug("synchronize");
		if(!blocking[id]) {
			processQueue(id);
		}
	};
	var stop = function(id) {
		if(!id) id = "default";
		blocking[id] = true;
		timeout[id] = setTimeout(function(){start(id);}, asyncTimeout * 1000);
		printDebug("stop");
	};
	var start = function(id) {
		if(!id) id = "default";
		if(timeout[id])
			clearTimeout(timeout[id]);
		blocking[id] = false;
		printDebug("start");
		processQueue(id);
	};
	var process = function(id) {
		if(!id) id = "default";
		while(queue.length && activeLoads == 0) {
			var call = queue[0];
			queue = queue.slice(1);
			call();
		}
	};
	var processQueue = function(id) {
		if(!id) id = "default";
		while(queues[id].length && !blocking[id]) {
			printDebug("process");
			var call = queues[id][0];
			queues[id] = queues[id].slice(1);
			call();
		}
	};
	var registerLoad = function() {
		activeLoads++;
		var loadID = getNextLoadID();
		timeout[loadID] = setTimeout(function(){loadComplete(loadID);}, asyncTimeout * 1000);
		return loadID
	};
	var loadComplete = function(loadID) {
		if(timeout[loadID])
			clearTimeout(timeout[loadID]);
		activeLoads--;
		process();
	};
	var getNextLoadID = function() {
		return loadID++;
	};
	var printDebug = function(caller) {
		if(this.debug) {
			for(var i=0; i<this.queueIds.length; i++) {
				$("#debug").append(caller+": queue id: "+this.queueIds[i]+" queue length: "+this.queues[this.queueIds[i]].length+" blocking: "+this.blocking[this.queueIds[i]]+"<br/>");
			}
			$("#debug").append("<br/>");
		}
	};
}
