//////////////////////////////////////////////////////
// Wrapper for SCORM /////////////////////////////////
//////////////////////////////////////////////////////
/*
 *	Last changes by : lkranabetter
 *	needs:
 * 		jsBitLib/time/timeManager.js
 * 
 */

scormManager = {
		
	//////////////////////////////////////////////
	//////// CONFIGURATION DEFAULT VALUES ////////
	//////////////////////////////////////////////
	debugMode: false
	,sendPercents: false
	,LESSON_STATUS_FAILURE: "incomplete"  
	,LESSON_STATUS_SUCCESS: "completed"
	,rememberLastProgress: true
	
	//////////////////////////////////////////////
	//////// LMS DEFAULT VALUES //////////////////
	//////////////////////////////////////////////
	,userName: 			"user"
	,userId: 			"userId"
	,masteryScore: 		"75"		// Must be string; supposed to come from LMS as a string
	,maxPoints: 		0			// Must be number
	,minPoints: 		0			// Must be number
	,userPoints: 		"0"			// Must be string; supposed to come from LMS as a string
	,startSession: 		null
	,timeLimit: 		"2700" 		// Format string "01:30:00" // Player should get seconds
	,lastLocation: 		"1"			// Must be string; supposed to come from LMS as a string
	,lastAudioPref: 	"ON"
	,suspendData:		""
	,lessonStatus: 		""
	
	// To store olp statistics in LMS
	,objectivesCOL:		[]
	
	,API: {
		handle: null
		,isFound: false
		,isConnected: false
		,version: ""
	}
	
	,lastError: {
		errorCode: 0
		,errorDesc: ""
	}
	
	,parameters: {
		1.2: {
			status: 		"cmi.core.lesson_status"
			,location: 		"cmi.core.lesson_location"
			,duration: 		"cmi.core.session_time"
			,maxPoints: 	"cmi.core.score.max"
			,minPoints: 	"cmi.core.score.min"
			,userPoints: 	"cmi.core.score.raw"
			,masteryScore: 	"cmi.student_data.mastery_score"
			,userAudioPref: "cmi.student_preference.audio"
			,userName:		"cmi.core.student_name"
			,userId: 		"cmi.core.student_id"
			,timeLimit: 	"cmi.student_data.max_time_allowed"
			,suspendData: 	"cmi.suspend_data"
			,sitosVersion:	"cmi.sitos._version"
			,objectives:	"cmi.objectives"
			,exit:			"cmi.core.exit"
		}		

		,2004: {
			status: 		"cmi.completion_status"
			,location: 		"cmi.location"
			,duration: 		"cmi.session_time"
			,maxPoints: 	"cmi.score.max"
			,minPoints: 	"cmi.score.min"
			,userPoints: 	"cmi.score.raw"
			,masteryScore: 	"cmi.scaled_passing_score"
			,userAudioPref: "cmi.learner_preference.audio_level"
			,userName:		"cmi.learner_name"
			,userId: 		"cmi.learner_id"
			,timeLimit: 	"cmi.max_time_allowed"
			,suspendData: 	"cmi.suspend_data"
			,sitosVersion: 	"cmi._version"
			,objectives:	"cmi.objectives"
			,exit:			"cmi.exit"
		} 
	}
	////////////////////////////////////////////////////////////////////
	////////////// Public methods //////////////////////////////////////
	////////////////////////////////////////////////////////////////////
	,initialize: function(){
		this._findAPI(window);
		//this._getAPI();
		this._connect();
	}
	
	,getDefaultValue: function(paramAlias){
		var defaultValues = {
			"status": 			this.lessonStatus	
			,"location": 		this.lastLocation
			,"duration": 		this._getSessionDurationString()
			,"maxPoints": 		this.maxPoints
			,"minPoints": 		this.minPoints
			,"userPoints": 		this.userPoints
			,"masteryScore": 	this.masteryScore
			,"userAudioPref": 	this.lastAudioPref
			,"userName":		this.userName
			,"userId": 			this.userId
			,"timeLimit": 		this.timeLimit
			,"suspendData": 	this.suspendData
			,"objectives":		this.objectivesCOL
		}
		
		if(typeof defaultValues[paramAlias] !== "undefined"){
			return defaultValues[paramAlias];
		}
	}
	
	,getParam: function(paramAlias){
		if(typeof paramAlias !== "string"){return}
		if(!this.API.isConnected){return this.getDefaultValue(paramAlias)}
		var traceMsgPrefix = "GetParam(" +paramAlias +") (alias)";
	    var value = "";
		var success = false;
		// Find the right param given an alias
		var parameter = this.parameters[this.API.version][paramAlias]
	    if(typeof parameter === "string" && parameter !== ""){  
	        var API = this._getHandle();
			if(API){
				switch(this.API.version){
					case "1.2" : value = API.LMSGetValue(parameter); break;
					case "2004": value = API.GetValue(parameter); break;
				}
	        }else {
	            this.trace(traceMsgPrefix +"failed: API is null.");
	        }
			
	    } else {
			this.trace(traceMsgPrefix +"failed: API connection is inactive or parameter is invalid");
	    }
		// Special params
		if(paramAlias == "userAudioPref"){
			switch (this.API.version){
				case "1.2":
					value = (value != "-1") ? "ON" : "OFF";
				break;
				
				case "2004":
					value = (value != "0") ? "ON" : "OFF";
				break;
				
				default:
					value = "ON";
				break;
			}
			this.lastAudioPref = value;
		}
		
		if(paramAlias == "masteryScore"){
			if(value == "" || typeof value === "undefined"){
				value = this.masteryScore;
			}
			this.masteryScore = Number(value);
		}
		
		if(paramAlias == "timeLimit"){
			if(value == "" || typeof value === "undefined"){
				value = this.timeLimit;
			}
			if(typeof value == "string" && value.indexOf(":") != -1){
				var aTime = value.split(":");
				var hSconds = Number(aTime[0])*3600;
				var mSconds = Number(aTime[1])*60;
				var sSconds = Number(aTime[2]);
				value = this.timeLimit = hSconds+mSconds+sSconds;				
			}
		}
		
		if(paramAlias == "location"){
			if(value == "" || typeof value === "undefined"){
				value = this.lastLocation;
			}
		}

		if(paramAlias == "userPoints"){
			var tmpValue;
			if(value == "" || typeof value === "undefined"){
				tmpValue = "0";
			}else{
				if(this.sendPercents){
					var percent = Number(value);
					var maxPoints = Number(this.getParam("maxPoints"));
					var rawPoints = Math.round(percent*maxPoints/100);
					tmpValue = String(rawPoints);
				}else{
					tmpValue = Number(value);
				}
			}
			this.userPoints = value = String(tmpValue);
		}

		// Special params
		this.trace(traceMsgPrefix + " "+ String(value));
		return String(value);
	}
	
	,setParam: function(paramAlias, value){
		
		var traceMsgPrefix = "SetParam";
		if(!this.API.isConnected){return false;}
		if(typeof paramAlias !== "string" || paramAlias === ""){return false}
		var parameter = this.parameters[this.API.version][paramAlias];
		if(typeof parameter !== "string"){return false}
		
		//this.trace(traceMsgPrefix+ " " +parameter +" with value: "+value);
		switch(paramAlias){
			case "duration":
				value = this._getSessionDurationString();
			break;
			
			case "maxPoints":
				this.maxPoints = Number(value); 
			break;
			
			case "minPoints":
				this.minPoints = Number(value);
			break;
			
			case "userAudioPref":
				value = this._getUserAudioPrefString(value);
			break;
			
			case "userPoints":
				// Check here lesson status. When "completed", make nothing
				//if(this.getParam("status") === this.LESSON_STATUS_SUCCESS){return false;}
				// Check rememberLastProgress is true and user points less then last ones, make nothing
				if(this.rememberLastProgress && value <= this.getParam("userPoints")){return false;}
				this.userPoints = Number(value);
				
				// :UGLY: But the only way that some LMS seem to handle fine the points,
				// is sending the percent directly.
				if(this.sendPercents){
					var masteryPercent = this.masteryScore;
					 switch(this.API.version){
					 	case "2004":
							percent = Math.round(Number(masteryPercent*100));
						break;
					 	case "1.2":
						default:
							percent = Math.round(Number(masteryPercent));
						break;	
					 }
					 if(typeof this.userPoints !== "number" 
						|| typeof this.maxPoints !== "number" 
						|| typeof percent !== "number"){return false}
					// Forced passed lesson
					if(this.maxPoints == 0 || this.userPoints == 1){
						value = "100";
					}else{
						value = String(Math.round((this.userPoints/this.maxPoints)*100));						
					}
					// If value is is more than 100 on sendPercents true send 100
					value = (Number(value) > 100) ? "100" : value;	
				}
			break;
			
			case "status":
				// Check here the lesson status. Only allow set when lesson status != "completed"
				if(this.getParam("status") === this.LESSON_STATUS_SUCCESS){
					return false;
				}else{
					var lessonStatus = this.LESSON_STATUS_FAILURE;
					var percent = this.masteryScore;
					/* // Disabled because mastery Score should come from configParams
					if(this.API.version == "1.2"){
						if(this.getParam("masteryScore") !== ""){
							percent = Math.round(Number(this.getParam("masteryScore")));
						}
					}else if(this.API.version == "2004"){
						if(this.getParam("masteryScore") !== ""){
							percent = Math.round(Number(this.getParam("masteryScore")*100));
						}
					}
					*/
					// When lesson is not completed, instead of "failed" we set it to incomplete to manage the lesson status in some LMS showing the lesson status
					// As requested norbert and users with low self-confidence.
					this.trace("userpoints:"+this.userPoints);
					this.trace("maxpoints:"+this.maxPoints);
					this.trace("percent:"+percent);
					var userPercent = Math.round((this.userPoints/this.maxPoints)*100);
					
					// Switch in config the kinds of lesson status
				    var lessonStatus = (userPercent>=percent) ? this.LESSON_STATUS_SUCCESS : this.LESSON_STATUS_FAILURE;
			        // Exception: when calling forcePassedLesson, we have to put the lesson status
				    if(typeof value === "string" && value == this.LESSON_STATUS_SUCCESS){
					   lessonStatus = this.LESSON_STATUS_SUCCESS;
					}
				}
				value = this.lessonStatus = lessonStatus;				
			break;
			
			case "location":
				// this.lastLocation = Number(value);
				this.lastLocation = String(value); 
			break;
			
			case "suspendData":
				value = String(value);
			break;

			case "duration":
				value = this._getSessionDurationString();
			break; 
			
			default:
				value = "";
			break;
		}
		
		if(this.API.isConnected){  
        	var API = this._getHandle();
			if(API){
				// Setting always strings
				if (typeof value !== "string") {
					value = String(value);
				}
				this.trace(parameter+" with value "+value);
				switch(this.API.version){
					case "1.2" : success = API.LMSSetValue(parameter, value); break;
					case "2004": success = API.SetValue(parameter, value); break;
				}
        	}else {
            	this.trace(traceMsgPrefix +"failed: API is null.");
        	}
    	} else {
        	this.trace(traceMsgPrefix +"failed: API connection is inactive");
    	}
		this.trace(traceMsgPrefix + " value: "+value+" success: "+ String(success));
		return success;
	}
	
	,saveData: function(){
		var	traceMsgPrefix = "scormManager.saveData: ";
		var success = false;
	    if(this.API.isConnected){
	        var API = this._getHandle();
	        if(API){
				switch(this.API.version){
					case "1.2" : success = API.LMSCommit(""); break;
					case "2004": success = API.Commit(""); break;
				}
	
	        } else {
				this.trace(traceMsgPrefix +": API not found.");
	        }
	
	    } else {
	        this.trace(traceMsgPrefix +": API connection is inactive.");
	    }
		this.trace(traceMsgPrefix + " "+ success);
	    return success;
	}
	
	,finish: function(){
		var traceMsgPrefix = "scormManager.finish: ";
		var success = false;
		if(this.API.isConnected){
			// Saving data
			var saveSuccess = this.saveData();
		 	var API = this._getHandle();
		 	if(API){
			 	this.setParam("exit", "");
			    switch(this.API.version){
			       case "1.2" : success = API.LMSFinish(""); break;
			       case "2004": success = API.Terminate(""); break;
			    }
			    this.trace(traceMsgPrefix + " "+ success);
		 	}
		}
   	}
	
	////////////////////////////////////////////////////////////////////
	////////////// Private methods //////////////////////////////////////
	////////////////////////////////////////////////////////////////////
	,_connect: function(){
		
		if(this.startSession === null){
			this.startSession = new Date();			
		}

		var	traceMsgPrefix = "SCORM.connection.initialize ";
		var completionStatus = "";
		var success = false;
		
	    if(!this.API.isConnected){
	        var API = this._getHandle();
	        
	        if(API){
				switch(this.API.version){
					case "1.2" : success = API.LMSInitialize(""); break;
					case "2004": success = API.Initialize(""); break;
				}
	
	            if(success){
					
	                this.API.isConnected = true;
					
					// Currently deactivated
					// TODO: Test
					if(this.autoHandleCompletionStatus){				
						//Automatically set new launches to incomplete 
						//completionStatus = this.handleCompletionStatus("get");
						if(completionStatus){
							switch(completionStatus){
								
								//Both SCORM 1.2 and 2004
								case "not attempted": 
									this.handleCompletionStatus("set", "incomplete"); 
									break;
								
								//SCORM 2004 only
								case "failed":
									this.handleCompletionStatus("set", "incomplete"); 
									break;
								
								//Additional options, presented here in case you'd like to use them
								//case "completed"  : break;
								//case "incomplete" : break;
								//case "passed"     : break;	//SCORM 1.2 only
								//case "failed"     : break;	//SCORM 1.2 only
								//case "browsed"    : break;	//SCORM 1.2 only	
								}	
							}	
						}
	            } else {
					this.trace(traceMsgPrefix +"failed: No response from server.");	
	            }
	        } else {
	            this.trace(traceMsgPrefix +"failed: API is null.");
	        }
	    } else {
	          this.trace(traceMsgPrefix +"aborted: Connection already active.");    
			var API = this._getHandle();
	        if(API){
				switch(this.API.version){
					case "1.2" : success = API.LMSFinish(""); break;
					case "2004": success = API.Terminate(""); break;
				}
			}
			this._connect();
	     }
		 
	     return success;
	}
	
	// Will always look for the API as an object of window.
	,_findAPI: function(win){
		var findAPITries = 1;
		while ((win.API==null) &&
			(win.parent!=null) &&
			(win.parent!=win)) {
			findAPITries++;
			if(findAPITries>7) {
				alert("Die API konnte nicht gefunden werden.. zu tief verschachtelt");
				return null;
			}
			win=win.parent;
		}
		if(win.API != null)
		{
			this.API.isFound = true;
			this.API.handle = win.API;
			this.API.version = "1.2";
		}
		return win.API;
	}	
	
	,_getAPI: function(){
		
		var theAPI = this._findAPI();
		
		if( (theAPI == null) && (window.opener != null) && (typeof(window.opener) != "undefined") ){
			theAPI = this._findAPI(window.opener);
		}
		
		if(theAPI == null){
			this.trace("Sorry, but i am unable to find an API adapter.");	
		}
	}
	
	,_getHandle: function(){
		if(this.API.isFound && this.API.handle !== null)
		{
			return this.API.handle;
		}
		else
		{
			var theAPI = this._findAPI(window);
			if((theAPI == null) && (window.opener != null) && (typeof(window.opener)!="undefined")) {
				theAPI= this._findAPI(window.opener);
			} 
			if(theAPI==null) 
			{
				this.trace("Der API-Adapter konnte nicht gefunden werden.");
			}
			else
			{
				this.API.isFound = true;
				this.API.handle = theAPI;
			}
			return this.API.handle;
		}	
	}	
	
	,_getSessionDurationString: function(){
		
		var sessionDuration = "";
		var startMs = Date.parse(this.startSession);
		var exitDate = new Date(); // NOW
		var exitMs = Date.parse(exitDate);
		
		// Needs timeManager object to work
		var oPrettyDate = timeManager.millisecondsToPrettyDate(exitMs-startMs);

		switch(this.API.version){
			case "1.2":
				sessionDuration = oPrettyDate.hours+":"+oPrettyDate.minutes+":"+oPrettyDate.seconds;
			break;
			
			// ISO format for SCORM 2004
			case "2004":
				sessionDuration ="PT"+oPrettyDate.hours+"H"+oPrettyDate.minutes+"M"+oPrettyDate.seconds+"S";
			break;
		}
		return sessionDuration;
	}
	
	// Unused so far
	,_handleCompletionStatus: function(){}	
	
	,_getUserAudioPrefString: function(audioStatus){
		
		var audioValue = "";
		switch(this.API.version){
			case "1.2":
				// 0: no change
				// 1-100: volume level
				// -1: off 
				audioValue = (audioStatus === "ON") ? "100" : "-1"; 
			break;
			case "2004":
				// 0: Infinite attenuation a.k.a. off
				// 1: No changes in attenuation or amplification
				audioValue = (audioStatus === "ON") ? "1" : "0";
			break;
		}
		return audioValue;
	}
	
	//////////////////////////////////////////////////////
	// OLP used from courseManager  //////////////////////
	//////////////////////////////////////////////////////
	
	,setObjectives: function(objectivesCOL){
		this.trace("passing objectives to RTE");
		for(var i=0; i<objectivesCOL.length; i++){
			var currentObjective = objectivesCOL[i];
			
			this.setObjective(	i,
								currentObjective.id, 
								currentObjective.title, 
								currentObjective.score,
								currentObjective.status,
								currentObjective.description);
		}
		
	}
	
	,setNonAliasedParam: function(paramName, paramValue){
		var traceMsgPrefix = "SetParam";
		if(typeof paramName !== "string"){return false}
		if(this.API.isConnected){  
        	var API = this._getHandle();
			if(API){
				// Setting always strings
				if (typeof paramValue !== "string") {
					//paramValue = String(paramValue);
				}
				this.trace(paramName+" with value "+paramValue);
				switch(this.API.version){
					case "1.2" : success = API.LMSSetValue(paramName, paramValue); break;
					case "2004": success = API.SetValue(paramName, paramValue); break;
				}
        	}else {
            	this.trace(traceMsgPrefix +"failed: API is null.");
        	}
    	} else {
        	this.trace(traceMsgPrefix +"failed: API connection is inactive");
    	}
    }

	,getNonAliasedParam: function(paramName){
		var traceMsgPrefix = "Get unaliased param";
		if(typeof paramName !== "string"){return false}
		var ret = "";
		if(this.API.isConnected){  
        	var API = this._getHandle();
			if(API){
				// Setting always strings
				switch(this.API.version){
					case "1.2" : ret = API.LMSGetValue(paramName); break;
					case "2004": ret = API.GetValue(paramName); break;
				}
        	}else {
            	this.trace(traceMsgPrefix +"failed: API is null.");
        	}
    	} else {
        	this.trace(traceMsgPrefix +"failed: API connection is inactive");
    	}
		return ret;
    }
	
	,setObjective: function(itemIndex, itemId, itemTitle, itemScore, itemStatus, itemDescription){
		this.trace("setting objective:"+itemIndex+" id:"+itemId);
		
		if (typeof scormManager.setNonAliasedParam !== "function"){return false}

		// ID for both SITOS and CMI
		scormManager.setNonAliasedParam("sitos.objectives."+itemIndex+".id", itemId);
		scormManager.setNonAliasedParam("cmi.objectives."+itemIndex+".id", itemId);

		// Title only for SITOS
		scormManager.setNonAliasedParam("sitos.objectives."+itemIndex+".title", itemTitle);

		// Points are for both CMI and SITOS
		scormManager.setNonAliasedParam("sitos.objectives."+itemIndex+".score.raw", itemScore.raw);
		scormManager.setNonAliasedParam("sitos.objectives."+itemIndex+".score.min", itemScore.min);
		scormManager.setNonAliasedParam("sitos.objectives."+itemIndex+".score.max", itemScore.max);
		scormManager.setNonAliasedParam("cmi.objectives."+itemIndex+".score.raw", itemScore.raw);
		scormManager.setNonAliasedParam("cmi.objectives."+itemIndex+".score.min", itemScore.min);
		scormManager.setNonAliasedParam("cmi.objectives."+itemIndex+".score.max", itemScore.max);

		// Status is for both CMI and SITOS
		if(this.API.version == "1.2"){
			scormManager.setNonAliasedParam("sitos.objectives."+itemIndex+".status", itemStatus.success);
			scormManager.setNonAliasedParam("cmi.objectives."+itemIndex+".status", itemStatus.success);			
		}else{
			scormManager.setNonAliasedParam("sitos.objectives."+itemIndex+".success_status", itemStatus.success);
			scormManager.setNonAliasedParam("cmi.objectives."+itemIndex+".completion_status", itemStatus.completion);
		}
		
		// Description is for both CMI and SITOS
		// scormManager.setNonAliasedParam("sitos.objectives."+itemIndex+".description", itemDescription);
		scormManager.setNonAliasedParam("cmi.objectives."+itemIndex+".description", itemDescription);	
	}
	
	// Trace for debug
	,trace : function(info){
		if (this.debugMode === true) {
			var tracePrefix = "[scormManager] ";
			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
scormManager = window.scormManager || {};