courseManager = {
	
	//////////////////////////////////////////////
	//////// CONFIGURATION DEFAULT VALUES ////////
	//////////////////////////////////////////////

	debugMode: false
	,packageId: ""
	,packagePath: "../../../package"
	,masteryScore: "75"
	,courseTitle: "Bitmedia"
	
	// COURSE ITEMS COLLECTION
	,itemsCOL: []
	
	// LEarnpath items collection
	,lpItemsCOL: []
	
	// Objectives collection
	,objectivesCOL: []
	
	// CURRENT ITEM BEING DISPLAYED
	,currentItem: {}
	
	// SMALL SET OF ROLLUP CONDITIONS SET WITHIN THE course.xml
	,rollupConditions: {}
	
	// User Interface elements
	,UI: {
		contentWrapper: null
		,movieWrapper: null
		,theMovie: null
		,buttonContents: null
		,buttonLearnpath: null
		,buttonReport: null
		,textReport: null
	}
	
	////////////////////////////////////////////////////////////////////
	////////////// Public methods //////////////////////////////////////
	////////////////////////////////////////////////////////////////////
	
	// Initialization interests of params either from LMS or if present from configParams
	// Be careful, order of interest params is important as well
	,initializationInterestParams:[
		{paramAlias: "debugMode",		prop: "debugMode",			target: "scormManager"}
		,{paramAlias: "debugMode",		prop: "debugMode",			target: "courseManager"}
		,{paramAlias: "sendPercents", 	prop: "sendPercents",		target: "scormManager"}
		,{paramAlias: "userName", 		prop: "userName",			target: "scormManager"}
		,{paramAlias: "userId", 		prop: "userId",				target: "scormManager"}
		,{paramAlias: "location", 		prop: "lastLocation",		target: "scormManager"}
		//,{paramAlias: "userAudioPref", 	prop: "lastAudioPref",	target: "scormManager"}
		,{paramAlias: "suspendData", 	prop: "suspendData",		target: "scormManager"}
		,{paramAlias: "userPoints", 	prop: "userPoints",			target: "scormManager"}
		,{paramAlias: "maxPoints", 		prop: "maxPoints",			target: "scormManager"}
		,{paramAlias: "minPoints", 		prop: "minPoints",			target: "scormManager"}
		,{paramAlias: "packageId", 		prop: "packageId",			target: "courseManager"}
		,{paramAlias: "masteryScore", 	prop: "masteryScore",		target: "courseManager"}
		,{paramAlias: "courseTitle",	prop: "courseTitle",		target: "courseManager"}
	]
	
	/**
	* Loads the course structure jso, merges the status of the course with the information stored within the CMI objectives and generates the contents tree
	* Loads the learnpath information to generate the learnpath tree
	*
	*/
	,initialize: function(){
		// Assign initializationInterestParams or configParams
		if(typeof scormManager.getParam !== "function"){return false}
		for(var i in this.initializationInterestParams){
			var prop = this.initializationInterestParams[i].prop;
			var param = this.initializationInterestParams[i].paramAlias;
			var tar = this.initializationInterestParams[i].target;
			//this.trace("init courseManager [param:"+param+" value:"+prop+" target:"+tar+"]");
      		if(typeof param === "string" && typeof prop === "string" && typeof tar === "string"){
				var value = scormManager.getParam(param, prop);
				// Check if a matching config value exists
				for(var j in configParams){
					var configValue = String(j);
					if(configValue == param || configValue == prop){
						// Overwrite maching config param in value
						value = configParams[param] || configParams[prop];
						this.trace("Param "+param+" is overwritten from config with value "+value);	
					}
				}		
      			//this.trace("initialised parameter:"+param+" with value:"+value);
				// write the param to the right target
      			if(tar == "scormManager" && typeof scormManager[prop] !== "undefined"){
      				scormManager[prop] = value;
      				this.trace("setted "+prop+" to scormManager "+scormManager[prop]+" with value: "+value);
      			}else if(tar == "courseManager" && typeof this[prop] !== "undefined"){
      				this[prop] = value;
      				this.trace("setted "+prop+" to courseManager "+this[prop]+" with value: "+value);	
				}else{
					this.trace("ERROR: setting param:"+param+" to "+tar+" failed");
				}
      		}
		}	
			
		this.itemsCOL = courseInfo.courseStructure.items;
		this.rollupConditions = courseInfo.courseStructure.rollupConditions;
		this.mergeObjectives();		
		this.lpItemsCOL = this.generateLearnpathItems(); 
		this.setPackagePath();
		this.setDocumentTitle();
		this.renderMovie();
		
		// Trace initialize output
		this.trace("Olp package Path: " + this.packagePath)
		this.trace("rollup conditions:");
		this.trace(this.rollupConditions);
		this.trace("items:");
		this.trace(this.itemsCOL);
	}
	
	,showContents :	function(){
		this.UI.theMovie.displayContents();
		this.UI.theMovie.expandParentTreeItem();
		this.UIDesigner("contents");
	}
	
	,showLearnpath : function(){
		this.UI.theMovie.displayLearnpath();
		this.UI.theMovie.expandParentTreeItem();
		this.UIDesigner("learnpath");
	}
	
	,showReport : function(){
		// Set true in closeItem for opening the report view
        this.closeItem(true);
        //this.UI.theMovie.displayContents();
		this.UIDesigner("report");
 	}
	
	,UIDesigner : function(UIState){
		this.UI.buttonLearnpath.className = courseManager.UI.buttonLearnpath.className.replace(" active","");
		this.UI.buttonContents.className = courseManager.UI.buttonContents.className.replace(" active", "");
		this.UI.buttonReport.className = courseManager.UI.buttonReport.className.replace(" active", "");
		switch(UIState){
			case "report" :
				this.UI.theMovie.style.visibility = "hidden";
				this.UI.textReport.style.visibility = "visible"; 
				this.UI.buttonReport.className += " active";
			break;
			case "learnpath" :
				this.UI.theMovie.style.visibility = "visible";
				this.UI.textReport.style.visibility = "hidden"; 
				this.UI.buttonLearnpath.className += " active";
			break;
			case "contents" :
				this.UI.theMovie.style.visibility = "visible";
				this.UI.textReport.style.visibility = "hidden"; 
				this.UI.buttonContents.className += " active";
			break;
			default : 
				this.trace("User interface design error");
			break;
		}
	}
	
	/**
	* Lessons status will be stored in the SCORM RTE objectives section.
	*
	*/
	,closeItem: function(showReport){
		if(scormManager.API.isFound){
			scormManager.setParam("location", this.currentItem.id);
			this.trace("API found, must store relevant data");
			this.generateLearnpathItems();
			// Refresh tree
			this.passItems();
			this.closeCourse();
			if(this.UI.theMovie.isLearnpathVisible())
		    {
			   this.UI.theMovie.displayLearnpath();
		    }
		}else{
			this.trace("API NOT FOUND, just go further in progress")
		}
		if(typeof showReport !== "undefined" && showReport === true){
			this.UI.contentWrapper.src = "nocontent.html";
			this.UIDesigner("report");
		}
	}
	
	
	/**
	* Stores all the lesson status objectives, Stores suspend data information
	*
	*/
	,closeCourse: function(){
	
		this.generateObjectives();

		// Storing objectives
		scormManager.setObjectives(this.objectivesCOL);
		
		// Storing last location
		scormManager.setParam("location", this.currentItem.id);
		// TODO: store learnpath XML string into session data

		// Storing session duration
		scormManager.setParam("duration");
		
		// Will determine the lesson_status for the SCO
		var isLessonPassed = this.handleRollup(); 
		var courseStatus = (isLessonPassed == false) ? "failed" : "passed";
		
		// Will set success_status for SCORM 2004 and status for SCORM 1.2
		scormManager.setParam("status", courseStatus);

		// Set completion status for SCORM 2004
		if(scormManager.API.version == "2004"){
			var completionStatus = (courseStatus == "passed") ? "complete" : "incomplete";
			scormManager.setParam("completion", completionStatus);
		}
		
		// Commit values
		scormManager.saveData();
	}
	
	,findItemById: function(itemId, setAsCurrent){
		
		var ret = null;
		
		for(var i in this.itemsCOL){
			if(typeof this.itemsCOL[i].id !== "undefined" && typeof this.itemsCOL[i].children !== "undefined"){
				var currentItem = this.itemsCOL[i];
				if(currentItem.id == itemId)
					ret = currentItem;
				for(var j in currentItem.children){
					if(typeof currentItem.children[j].id !== "undefined" && currentItem.children[j].id == itemId){
						currentItem.children[j].callPath = currentItem.id+"/";
						ret = currentItem.children[j];
					}
				}			
			}
		}
		
		this.currentItem = (setAsCurrent) ? ret : this.currentItem;
		return ret;
		
	}
	
	,launchItem: function(itemId){
		var item = (typeof itemId !== "undefined") ? this.findItemById(itemId, true) : this.currentItem;
		item.status.completion = "browsed";
		this.UI.contentWrapper.src = this.packagePath+"/"+this.getLaunchURL();
		scormManager.setParam("location", this.currentItem.id);
	}
	
	,getLaunchURL: function(itemId){
		// NO ID = current item
		var item = (typeof itemId !== "undefined") ? this.findItemById(itemId, false) : this.currentItem;
		return item.callPath+item.id+"/"+item.entryPointURL;
	}
	
	// If undefined get package id out of launch path and return the path
	,setPackagePath : function(){
		if(typeof this.packageId === "undefined" || this.packageId == ""){
			var packagePathA = document.location.pathname.split("/");
			this.packageId = packagePathA[packagePathA.length - 4];	
		}
		this.packagePath = "../../../"+this.packageId; 
	}
		
	,mergeObjectives: function(){
		
		// Merge objectives 
		if(scormManager.API.isConnected){
			var count = scormManager.getNonAliasedParam("cmi.objectives._count");
			for(var i=0; i<count;i++){
				var oId = scormManager.getNonAliasedParam("cmi.objectives."+i+".id");			
				var oMax = scormManager.getNonAliasedParam("cmi.objectives."+i+".score.max");
				var oRaw = scormManager.getNonAliasedParam("cmi.objectives."+i+".score.raw");
				var oStatus = "unknown";
				var oStatusSuccess = "";
				var oStatusCompletion = "";
				if(scormManager.API.version == "2004"){
					oStatusSuccess = scormManager.getNonAliasedParam("cmi.objectives."+i+".success_status");				
					oStatusCompletion = scormManager.getNonAliasedParam("cmi.objectives."+i+".completion_status");				
				}
				else{
					oStatus = scormManager.getNonAliasedParam("cmi.objectives."+i+".status");				
				}

				var item = this.findItemById(oId, false);
				if(item != null){
					item.score.max = oMax;
					item.score.raw = oRaw;
					if(scormManager.API.version == "2004"){
						item.status.completion = oStatusCompletion;
						item.status.success = oStatusSuccess;
					}else{
						if(item.score.max != 0 || item.score.raw != 0){
							item.status.completion = "browsed";
						}
						item.status.success = oStatus;									
					}
				}
			}
		}
			
		// Identify rollup items
		var rollupProgress = this.rollupConditions.rollupProgress;
		var masteryScore = this.rollupConditions.masteryScore;
		var itemIds = rollupProgress.itemIds;
		// getting id�s
		var arrItemIds = (itemIds.indexOf(",") != -1) ? rollupProgress.itemIds.split(",") : [itemIds];
		for(var i=0; i<arrItemIds.length; i++){
			var item = this.findItemById(arrItemIds[i]);
			if(item != null){
				item.rollupItem = true; // This is just so the flex app can choose the appropiate icon (remarked)
			}			
		}
	}
	
	,generateLearnpathItems: function(learnpathItemIds){

		var sd = scormManager.suspendData;
		var ids = (typeof sd !== "undefined") ? sd : "";
		
		// Trim spaces, remove duplicates
		var arrIds = ids.split(",");
		var trimIds = [];
		arrIds.each(function(id){trimIds.push(id.strip());});
		trimIds = trimIds.uniq();
		
		this.trace(trimIds);
		var learnpathItems = [];
		var that = this;
		this.itemsCOL.each(function(item){
			var oItem = {
				id: item.id
				,type: ""
				,title: item.title
				,description: item.description
				,children: []
			}
			var itemChildren = item.children;
			itemChildren.each(function(child){
				var oChild = {
					id: child.id
					,title: child.title
					,type: "lesson"
					,description: child.description
					,children: null
					,status: {
						completion: child.status.completion
						,success: child.status.success
					}
				};
				for(var i=0; i<trimIds.length; i++){
					if(oChild.id == trimIds[i]){
						oItem.children.push(oChild);
					}
				}
			});	
			if(oItem.children.length>0){
				learnpathItems.push(oItem);				
			}
		});
		this.trace("Generate learnpath items: " + learnpathItems);
		return learnpathItems;
	}
	
	,isRollupItem: function(itemId){
		var itemIds = this.rollupConditions.rollupProgress.itemIds;
		return (itemIds.indexOf(itemId) != -1);
		
	}

	/**
	* Passes each single item to the flex movie
	*
	*/
	,passItems: function(){

		this.UI.theMovie.resetItems();
		
		// Passing course items
		for(var i in this.itemsCOL){
			var item = this.itemsCOL[i];
			if(typeof item.id !== "undefined"){
				this.UI.theMovie.setItem(item);
			}
		}

		// Passing learnpath items
		for(var i in this.lpItemsCOL){
			var lpItem = this.lpItemsCOL[i];
			if(typeof lpItem.id !== "undefined"){
				this.UI.theMovie.setLpItem(lpItem);
			}
		}
		this.UI.theMovie.init();
		this.UI.theMovie.expandParentTreeItem();		
	}
	
	
	/**
	* Renders the flex movie passing the parameters needed via flashvars
	*
	*/
	,renderMovie: function(){
		var movieName = "OnlineLearnpathComponent";
		var so = new SWFObject("swf/OnlineLearnpathComponent.swf", movieName, "100%", "100%", "9", "#e9e9e9");
		so.write(this.UI.movieWrapper.id);				

		//this.UI.theMovie = (navigator.appName.indexOf("Microsoft") != -1) ? window[movieName] : document[movieName];
		this.UI.theMovie = (navigator.userAgent.indexOf("MSIE") != -1) ? window[movieName] : document[movieName];
		if (this.UI.theMovie == null) {
			alert("Error rendering the movie. Do you have Adobe Flash Player plugin installed? ");
		}
	}
	
	/**
	* Loops over the items collection to create a objectives collection ready to set in scorm model.
	*
	*/
	,generateObjectives: function(){
		this.trace("generating objectives");
		this.objectivesCOL = [];
		for(var i = 0; i<this.itemsCOL.length; i++){
			var currentItem = this.itemsCOL[i];
			if(typeof currentItem.children !== "undefined"){
				for(var j=0; j<currentItem.children.length;j++){
					var currentChild = currentItem.children[j];
					if(typeof currentChild.id !== "undefined"){
						var oObjective = {
							id: currentChild.id
							,title: currentChild.title
							,score: {
								max: currentChild.score.max,
								min: currentChild.score.min, 
								raw: currentChild.score.raw
							}
							,status: {
								completion: (currentChild.status.completion == "") ? "not attempted" : currentChild.status.completion,
								success: (currentChild.status.success == "") ? "failed" : currentChild.status.success
							}
							,description: currentChild.description
						}
						this.objectivesCOL.push(oObjective);	
					}
				}
			}
		}
		
		this.trace("Will set the following objectives");
		this.trace(this.objectivesCOL);
	}
	
	,handleLearnpath: function(learnpathItemIds){
		this.trace("***** storing learnpath *****");
		// just to make sure...
		var value = learnpathItemIds.toString();
		scormManager.setParam("suspendData", value);
		scormManager.suspendData = value;
		scormManager.saveData();
		this.lpItemsCOL = this.generateLearnpathItems();
		this.passItems();
		if(this.UI.theMovie.isLearnpathVisible())
		{
			this.UI.theMovie.displayLearnpath();
		}
	}
	
	///////////////////////////////////////////////////////
	///////////////// HANDLE ROLLUP ///////////////////////
	///////////////////////////////////////////////////////
	,handleRollup: function(){
		
		// handling progress
		var rollupProgress = this.rollupConditions.rollupProgress;
		var masteryScore = this.rollupConditions.masteryScore;
		var rollupStrategy = rollupProgress.strategy;
		var itemIds = rollupProgress.itemIds;
		// getting id�s
		var arrItemIds = (itemIds.indexOf(",") != -1) ? rollupProgress.itemIds.split(",") : [itemIds];
		
		// TODO: More rollup conditions?
		var rollupProgressPassed = this.handleRollupProgress(masteryScore, arrItemIds, rollupStrategy);

		return rollupProgressPassed;
	}
	
	,handleRollupProgress: function(masteryScore, arrItemIds, strategy){
		
		var swPass = true;
		var tmpMax = 0;
		var tmpRaw = 0;
		var tmpMin = 0;
		var numlessons = 0;
		var passedlessons = 0;
		for(var i=0; i<arrItemIds.length; i++){
			var currentItem = this.findItemById(arrItemIds[i]);
			numlessons++;
			tmpMax += currentItem.score.max;
			tmpMin += currentItem.score.min;
			tmpRaw += currentItem.score.raw;
			switch(strategy){
				case "COMPLETION":
					swPass = swPass && (currentItem.status.completion == "completed");
				break;
				
				case "SUCCESS":
					swPass = swPass && (currentItem.status.success == "passed");
					if(currentItem.status.success == "passed")
					{
						passedlessons++;
					}	
				break;
				
				
				//Old way ... delete if new is working fine
				/*
				case "COMPLETION":
					swPass = swPass && (currentItem.status.completion == "completed");
				break;
				
				case "SUCCESS":
					swPass = swPass && (currentItem.status.success == "passed");
				break;
				//TODO: Check if neccesary, because setting config ROLLUP_POLICY="pages" is allready existing
				/*
				case "BROWSED":
					swPass = swPass && (currentItem.status.completion == "browsed");
				break;
				*/	
			}	
		}
		if(arrItemIds.length>1)
		{
			scormManager.setParam("maxPoints", numlessons);
			scormManager.setParam("minPoints", 0);
			scormManager.setParam("userPoints", passedlessons);
		}
		else
		{
			scormManager.setParam("maxPoints", tmpMax);
			scormManager.setParam("minPoints", tmpMin);
			scormManager.setParam("userPoints", tmpRaw);
		}
		return swPass;
	}
	
	,startCourse: function(){
		var storedLessonlocation = scormManager.getNonAliasedParam("cmi.core.lesson_location");
		
		if(storedLessonlocation != ""){
			var lastBrowsedItem = this.findItemById(storedLessonlocation, false);
			if(lastBrowsedItem != null){
				this.launchItem(storedLessonlocation);
				this.UI.theMovie.setlaunchedItemId(storedLessonlocation);
			}
		}else{
			this.UI.contentWrapper.src = "nocontent.html";
		}	
	}
	
	///////////////////////////////////////////////////////
	///////////////// PLAYER CALLBACKS ////////////////////
	///////////////////////////////////////////////////////
	
	,setItemMaxPoints: function(points, itemId){
		this.trace("setting item maxpoints:"+points);
		var id = (typeof itemId !== "undefined") ? itemId : this.currentItem.id;
		var item = this.findItemById(id);
		item.score.max = points;
	}
	
	,setItemMinPoints: function(points, itemId){
		this.trace("setting item minpoints:"+points);
		var id = (typeof itemId !== "undefined") ? itemId : this.currentItem.id;
		var item = this.findItemById(id);
		item.score.min = points;
	}
	
	,setItemUserPoints: function(points, itemId){
		this.trace("setting item rawpoints:"+points);
		var id = (typeof itemId !== "undefined") ? itemId : this.currentItem.id;
		var item = this.findItemById(id);
		if(item.score.raw >= points)
			return false;
		item.score.raw = points;
		this.setItemStatus(id, false);
	}
	
	// Force passed to a lesson like chapter exercise by open it
	,forcePassedLesson: function(){
      this.setItemMaxPoints(1);
      this.setItemUserPoints(1);
   }
	
	// Force passed to a test passed (success passed) but remaining the test points as they are
	,forcePassedTest: function(){
		var item = this.findItemById(this.currentItem.id);
		item.status.success = "passed";
	}

	// Set on setItemUserPoints
	,setItemStatus: function(itemId, forcedPassed){
		var id = (typeof itemId !== "undefined" && itemId != "") ? itemId : this.currentItem.id;
		var item = this.findItemById(id);
		if(item.score.max > 0 && item.score.raw > 0){
			var percent = Math.round((item.score.raw*100)/item.score.max);
			item.status.success = (percent >= this.rollupConditions.masteryScore) ? "passed" : "failed";
		}
		// Set forcedPassed if is set to true
		if(forcedPassed === true){
			item.status.success = "passed";
			this.trace("Forced Passed Item " + id);	
		}
	}
	
	,setAudioPref: function(audioPref, itemId){
		scormManager.setParam("userAudioPref", audioPref);
		scormManager.saveData();
		this.trace("setting audio prefs (setAudioPref):"+audioPref);
	}
	
	,_isLocalStorageAvailable: function(){
		this.trace(typeof window.localStorage !== "undefined");
		return typeof window.localStorage !== "undefined";
	}
	
	,setUserNotes: function(userNotesStr){
		if(!this._isLocalStorageAvailable()){
			alert("Your notes cant be saved. Supported browsers are IE8, FF, Safari...");
			return false;
		}
		try{
			window.localStorage.setItem(this.currentItem.id, userNotesStr);
		}catch(e){
			alert("Ooops, something went wrong while storing your notes:"+e.toString());
		}
	}
	
	// Set document title
	,setDocumentTitle : function(){
		if (typeof this.courseTitle === "string" && this.courseTitle != ""){
			document.title = this.courseTitle;
		} else {
			document.tilte = "Bitmedia";
		}
	}
	
	,getUserNotes: function(){
		if(!this._isLocalStorageAvailable())
			return "";
		try{
			this.trace(window.localStorage.getItem(this.currentItem.id));
			return window.localStorage.getItem(this.currentItem.id);
		}catch(e){
			alert("Ooops, something went wrong while retrieving your notes:"+e.toString());
			return "";
		}
	}
	
	,getAudioPref: function(){
		return scormManager.getParam("userAudioPref");
	}
	
	// Trace for debug
	,trace : function(info){
		if (this.debugMode === true) {
			var tracePrefix = "[courseManager] ";
			if (window.console && window.console.firebug) { // Firebug
				if(typeof info == "string"){
					console.log(tracePrefix+info);
				}else{
					console.debug(tracePrefix+info);
				}
			}else if (window.console) { 					// Safari
				if(typeof info == "string"){
					window.console.log(tracePrefix+info);
				}else{
					//window.console.debug(tracePrefix+info);
				}
			}else { 										// Ugly browsers a.k.a IE any version
				alert(tracePrefix+info.toString());
			}
		}
	}
}

// Be sure object is present
courseManager = window.courseManager || {};