/*
 * LICENCE[[
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1/CeCILL 2.O
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is kelis.fr code.
 *
 * The Initial Developer of the Original Code is
 * sylvain.spinelli@kelis.fr
 *
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * or the CeCILL Licence Version 2.0 (http://www.cecill.info/licences.en.html),
 * in which case the provisions of the GPL, the LGPL or the CeCILL are applicable
 * instead of those above. If you wish to allow use of your version of this file
 * only under the terms of either the GPL, the LGPL or the CeCILL, and not to allow
 * others to use your version of this file under the terms of the MPL, indicate
 * your decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL, the LGPL or the CeCILL. If you do not
 * delete the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL, the LGPL or the CeCILL.
 * ]]LICENCE
 */
 
/** 
 scorm2k4 + scorm12 services

### Indique si un context SCORM est présent
isScorm2k4Active()
isScorm12Active()

### Récupère la référence vers l'objet JS API_1484_11 (ie 2004) ou API (ie 1.2).
getScorm2k4API()
getScorm12API()

*/
scServices.scorm2k4 = {

//2004
isScorm2k4Active : function() { return this._2k4Api != null; },
getScorm2k4API : function() { return this._2k4Api; },

//1.2
isScorm12Active : function() { return this._12Api != null; },
getScorm12API : function() { return this._12Api; },


//#### interne ####
xFindAPI : function (pWin, pName) {
	var vWin = pWin;
	var vDeep = 0;
	//on cherche l'API dans la hierarchie de la fenêtre en cours
	while(vWin[pName]==null && vWin.parent!=null && vWin.parent!=vWin) {
		if (vDeep++ > 20) break; //on limite la profondeur de l'exploration
		vWin = vWin.parent;
	}
	//On examine si nécessaire la fenêtre qui a ouvert celle en cours
	if (vWin[pName]==null && pWin.opener!=null && typeof(pWin.opener)!="undefined" ) {
		vWin = pWin.opener;
		vDeep=0;
		while(vWin[pName] == null && vWin.parent != null && vWin.parent != vWin) {
			if (vDeep++ > 20) return null;
			vWin = vWin.parent;
		}
	}
	return vWin[pName];
},

_2k4Api : null,
_12Api : null,
onLoad: function(){
	try {
		//######## Scorm 2k4 ##########
		this._2k4Api = this.xFindAPI(window, "API_1484_11");
		if(this._2k4Api) {
			this.checkError = function(pAlertUser){
				var vErrCode = this._2k4Api.GetLastError();
				if(vErrCode != "0") {
					var vMess = "SCORM 2004 error "+ vErrCode + " : " + this._2k4Api.GetErrorString(vErrCode) + "\n" + this._2k4Api.GetDiagnostic(vErrCode);
					if(pAlertUser) alert(vMess);
					return vMess;
				}
				return null;
			}
			//Init...
			if(this._2k4Api.Initialize("") != "true") {
				this.checkError(true);
				this._2k4Api = null;
			}
			return;
		}
	}catch(e){
		alert("Initialize SCORM 2004 failed." + (("description" in e) ? e.description : e));
		this._2k4Api = null;
	}
	try {
		//######## Scorm 1.2 ##########
		this._12Api = this.xFindAPI(window, "API");
		if(this._12Api) {
			this.checkError = function(pAlertUser){
				var vErrCode = this._12Api.LMSGetLastError();
				if(vErrCode != "0") {
					var vMess = "SCORM 1.2 error "+ vErrCode + " : " + this._12Api.LMSGetErrorString(vErrCode) + "\n" + this._12Api.LMSGetDiagnostic(vErrCode);
					if(pAlertUser) alert(vMess);
					return vMess;
				}
				return null;
			}
			//Init...
			if(this._12Api.LMSInitialize("") != "true") {
				this.checkError(true);
				this._12Api = null;
			}
			return;
		}
	}catch(e){
		alert("Initialize SCORM 1.2 failed." + (("description" in e) ? e.description : e));
		this._12Api = null;
	}
},
loadSortKey: "0scorm",
onUnload: function(){
	if(this._2k4Api) {
		this._2k4Api.Commit("");
		this._2k4Api.Terminate("");
		this._2k4Api = null;
	}
	if(this._12Api) {
		this._12Api.LMSCommit("");
		this._12Api.LMSFinish("");
		this._12Api = null;
	}
},
unloadSortKey: "4scorm"

};

//Service Scorm 1.2
scServices.scorm12 = scServices.scorm2k4;

//Abonnements aux events onLoad et onUnload.
scOnLoads[scOnLoads.length] = scServices.scorm2k4;
scOnUnloads[scOnUnloads.length] = scServices.scorm2k4;






//########################
//Service suspendDataStorage
scServices.suspendDataStorage = {
	
/** Lecture d'une variable de l'instance. Retourne null si la variable n'existe pas. 
 * @param pFields tableau de la hierarchie des champs.
 */
getVal: function(pFields) {
	var vCur = this._Fields;
	for(var i=0, imax=pFields.length; i < imax; i++) {
		var vF = vCur[pFields[i]];
		if(vF == null) return null;
		vCur = vF;
	}
	return vCur;
},

/** Stockage d'une variable dans l'instance. 
 * Retourne true si la valeur a été modifiée, false sinon.
 * @param pFields tableau de la hierarchie des champs.
 */
setVal : function (pFields, pVal) {
	var vCur = this._Fields;
	var imax = pFields.length-1;
	for(var i=0; i<imax; i++) {
		var vF = vCur[pFields[i]];
		if(vF == null) vCur = vCur[pFields[i]] = {}; else vCur = vF;
	}
	if( ! (pFields[imax] in vCur) || vCur[pFields[imax]] != pVal) {
		vCur[pFields[imax]] = pVal;
		this._Modif = true;
		return true;
	}
	return false;
},

/* Suppression d'une variable dans l'instance. 
 * Retourne true si la valeur a été supprimée, false si pCd n'existait pas.
 * @param pFields tableau de la hierarchie des champs.
 */
removeVal : function (pFields) {
	var vCur = this._Fields;
	var imax = pFields.length-1;
	for(var i=0; i<imax; i++) {
		var vF = vCur[pFields[i]];
		if(vF == null) return false;
		vCur = vF;
	}
	if(vCur[pFields[imax]]) {
		this._Modif = true;
		return delete vCur[pFields[imax]];
	}
	return false;
},

/* Force un enregistrement immédiat
 */
commit : function() {
	if( ! this._Modif) return;
	if(scServices.scorm2k4 && scServices.scorm2k4.isScorm2k4Active()) {
		var vApi = scServices.scorm2k4.getScorm2k4API();
		vApi.SetValue("cmi.suspend_data", this.xSaveObjJs(this._Fields));
		vApi.Commit("");
	} else if(scServices.scorm12 && scServices.scorm12.isScorm12Active()) {
		var vApi = scServices.scorm12.getScorm12API();
		vApi.LMSSetValue("cmi.suspend_data", this.xSaveObjJs(this._Fields));
		vApi.LMSCommit("");
	}
},

/* interne */
_Fields: {},
_Modif: false,

//Serialize un objet comportant des données au format JS
xSaveObjJs : function(pObj){
	var vBuf="";
	for (var vKey in pObj){
		if(vBuf!="") vBuf+=",";
		var vObj = pObj[vKey];
		if(vObj instanceof Object){
			vBuf+="'"+vKey+"':{"+this.xSaveObjJs(vObj)+"}";
		} else {
			var vVal = escape(vObj);
			if(vVal==vObj) {
				vBuf+="'"+vKey+"':'"+vVal+"'";
			} else {
				vBuf+="'"+vKey+"':unescape('"+vVal+"')";
			}
		}
	}
	return vBuf;
},


onLoad: function(){
	var vApi;
	if(scServices.scorm2k4 && scServices.scorm2k4.isScorm2k4Active()) {
		vApi = scServices.scorm2k4.getScorm2k4API();
		eval("this._Fields={"+vApi.GetValue("cmi.suspend_data")+"};");
	} else if(scServices.scorm12 && scServices.scorm12.isScorm12Active()) {
		vApi = scServices.scorm12.getScorm12API();
		eval("this._Fields={"+vApi.LMSGetValue("cmi.suspend_data")+"};");
	}
	//Mise en place de la sauvegarde automatique.
	if(vApi) this._ThreadCommit = window.setInterval("scServices.suspendDataStorage.commit();", 60000);
},
loadSortKey: "1suspendDataStorage",
onUnload: function(){
	try{window.clearInterval(this._ThreadCommit);}catch(e){};
	this.commit();
},
unloadSortKey: "3suspendDataStorage"
};

scOnLoads[scOnLoads.length] = scServices.suspendDataStorage;
scOnUnloads[scOnUnloads.length] = scServices.suspendDataStorage;




/* ########################
//Service locationStorage
scServices.locationStorage = {

	getLocation : function(){
		if(scServices.scorm2k4 && scServices.scorm2k4.isScorm2k4Active()) {
			return scServices.scorm2k4.getScorm2k4API().GetValue("cmi.location");
		} else if(scServices.scorm12 && scServices.scorm12.isScorm12Active()) {
			return scServices.scorm12.getScorm12API().LMSGetValue("cmi.core.lesson_location");
		}
		return null;
	},
	setLocation : function(pLoc){
		if(scServices.scorm2k4 && scServices.scorm2k4.isScorm2k4Active()) {
			var vApi = scServices.scorm2k4.getScorm2k4API();
			vApi.SetValue("cmi.location", pLoc);
			//On ne commit pas pour ne pas allourdir, pas très grave si info perdue
			//vApi.Commit("");
		} else if(scServices.scorm12 && scServices.scorm12.isScorm12Active()) {
			var vApi = scServices.scorm12.getScorm12API();
			vApi.LMSSetValue("cmi.core.lesson_location", pLoc);
			//On ne commit pas pour ne pas allourdir, pas très grave si info perdue
			//vApi.LMSCommit("");
		}
		return null;
	},
	
	onLoad: function(){
		var vLoc = this.getLocation();
		if(vLoc) {
			window.scUrlToLoad = vLoc;
			//On efface immédiatement pour le prochain lancement. 
			//La valeur devrait être réaffecté dès la 1ere page chargée.
			//En cas d'echec au chargement, le module est ainsi réinitialisé automatiquement et fonctionnera au prochain lancement.
			this.setLocation("");
		}
	},
	loadSortKey: "1locationStorage"
};
scOnLoads[scOnLoads.length] = scServices.locationStorage;
*/


//########################
//Service exitModeStorage
scServices.exitModeStorage = {

	/**
	 * @param pExitMode Valeurs possibles : "time-out", "suspend", "logout", "normal", ""
	 */
	terminate : function(pExitMode){
		this._done = true;
		if(scServices.scorm2k4 && scServices.scorm2k4.isScorm2k4Active()) {
			vApi = scServices.scorm2k4.getScorm2k4API();
			//session_time
			var vDur = Math.round( (new Date().getTime() - this._timeStart) / 1000 );
			var vH = vDur > 3600000 ? Math.floor(vDur / 3600000) : 0;
			var vM = Math.floor(vDur / 60000) % 60;
			var vS = Math.floor(vDur / 1000) % 60;
			var vTime = "PT";
			if(vH>0) vTime = vTime + vH + "H";
			if(vM>0) vTime = vTime + vM + "M";
			if(vS>0 || vTime == "PT") vTime = vTime + vS + "S";
			vApi.LMSSetValue("cmi.session_time", vTime);
			//exit mode
			vApi.SetValue("cmi.exit", pExitMode || "");
			vApi.Commit("");
		} else if(scServices.scorm12 && scServices.scorm12.isScorm12Active()) {
			vApi = scServices.scorm12.getScorm12API();
			//session_time
			var vDur = Math.round( (new Date().getTime() - this._timeStart));
			var vH = vDur > 3600000 ? Math.floor(vDur / 3600000) : 0;
			var vM = Math.floor(vDur / 60000) % 60;
			var vS = Math.floor(vDur / 1000) % 60;
			var vMs = Math.round(Math.floor(vDur % 1000)/10);
			var vTime = (vH<10 ? "0"+vH : vH) + ":" + (vM<10 ? "0"+vM : vM) + ":" + (vS<10 ? "0"+vS : vS) + "." + (vMs<10 ? "0"+vMs : vMs);
			vApi.LMSSetValue("cmi.core.session_time", vTime);
			//exit mode
			if( ! pExitMode) pExitMode = "";
			if(pExitMode == "time-out") pExitMode = "timeout";
			vApi.LMSSetValue("cmi.core.exit", pExitMode);
			vApi.LMSCommit("");
		}
	},
	
	_timeStart : 0,
	_done : false,
	onLoad: function(){
		this._timeStart = new Date().getTime();
	},
	loadSortKey: "1exitModeStorage",
	onUnload: function(){
		if(! this._done) this.terminate();
	},
	unloadSortKey: "3exitModeStorage"
};

scOnLoads[scOnLoads.length] = scServices.exitModeStorage;
scOnUnloads[scOnUnloads.length] = scServices.exitModeStorage;
