/*******************************************************************************
** initialisations
*******************************************************************************/
var NB_MAX_SIZE_SUSPEND_DATA = 1000;
var DEBUG_MODE = false;

//Liste des header des interactions envoyées par la player.
var aInteractionHeaders = ["interaction_id","type","result","weighting","latency","correct_response_text","student_response_text","description","id_theme"];

//Liste des headers des interaction de thème
var aThemeHeaders = ["title","score","status","time"];

//Variables de gestion des modes de tracking
var _sTrackingMode = ""; //Element de ["SCORM 1.2", "SCORM 1.3", "HACP"]
var _bOneShotTracking = false;
var _bInitialized = false;

//Variable de stockage des données de tracking
//Pour les envoyer à la fin
var tracking = new Object();
var aInteractionsList = new Array(); //Liste des interactions.
var aThemesList = new Array(); //Liste des thèmes.

var bTrackInteractions = false;		//flag : faut-il tracker les interactions ?
var aTrackInteractionsList = [];	//liste des champs supportés par le LMS

var API = null;			//Objet API délivré par une des fenetres parentes
var nFindAPITries = 0;  //Compteur de la profondeur de recherche de l'API dans la hierarchie des fenetres
var nCommitIntervalID;	//Identifiant du setInterval qui gère la commande de validation des données envoyées au LMS

var _iDataBlockRefIndex = 0;
var _aDataBlockRefs = ["cmi.suspend_data", "cmi.launch_data", "cmi.comments"]

/*******************************************************************************
**
** Function myLMSInitialize()
** Inputs:  none
** Return:  true si initialisation OK. Sinon false
**
** Description:
** Initialise le tracking avec le LMS en déterminant le protocole utilisé APIBased 1.2, APIBased 1.3 ou HACP
**
*******************************************************************************/
function myLMSInitialize() {
	//alert('myLMSInitialize');
	var buffer = false;
	
	var sResult = doLMSInitializeSCORM13();
	if (sResult == "false") sResult = doLMSInitializeSCORM12();
	if (sResult == "false") sResult = doLMSInitializeHACP();
	buffer = (sResult == "true");
	_bInitialized = buffer;

	return buffer;
}


/*******************************************************************************
**
** Function myLMSFinish()
** Inputs:  none
** Return:  le dernier code d'erreur
**
** Description:
** Envoi une dernière fois les données de tracking au LMS
** et met fin à la session de tracking en SCORM 1.2
**
*******************************************************************************/
function myLMSFinish() {
	var buffer = false;

	if (_bInitialized) {
		_bInitialized = false;

		//Si le tracking a lieu en une seule fois à la fin
		//C'est le moment pour l'envoyer au LMS
		if (_bOneShotTracking) {
			//Désactive le One Shot Tracking pour forcer l'envoi des données maintenant
			_bOneShotTracking = false;
			
			//Envoi l'ensemble des données stockées
			for (var str in tracking) myLMSSetValue(str, tracking[str]);
			
			//Force l'envoi du lesson_status en dernier pour être certain qu'il a au moins été mis à jour une fois (pour écraser le "not attempted")
			if (tracking["cmi.core.lesson_status"].toLowerCase().charAt(0) == "n") tracking["cmi.core.lesson_status"] = "incomplete"; 
			myLMSSetValue("cmi.core.lesson_status", tracking["cmi.core.lesson_status"]);
		}

		switch (_sTrackingMode) {
			case "SCORM 1.2": buffer = doLMSFinishSCORM12(); break;
			case "SCORM 1.3": buffer = doLMSFinishSCORM13(); break;
			case "HACP": buffer = doLMSFinishHACP(); break;
		}

	} else {
		buffer = true;
	}
	
	// on désactive le tracking qui est maintenant terminé
	_sTrackingMode = "";
	
	return buffer;
}

/*******************************************************************************
**
** Function getLMSParameters()
** Inputs:  none
** Return:  une chaine URL
**
** Description:
** Retourne des paramètres url_encoded avec des infos sur le tracking
**
*******************************************************************************/
function getLMSParameters() {
	var buffer = "";

	_iDataBlockRefIndex = 0;

	switch (_sTrackingMode) {
		case "SCORM 1.2": buffer = getLMSParametersSCORM12(); break;
		case "SCORM 1.3": buffer = getLMSParametersSCORM13(); break;
		case "HACP": buffer = getLMSParametersHACP(); break;
	}
	
	return buffer;
}

/*******************************************************************************
**
** Function myLMSSetValue()
** Inputs:  sElement, sValue 
** Return:	none
**
** Description:
** Envoi et met à jour la valeur de sElement sur le LMS
** Dans le cas du "One Shot Tracking", on stocke simplement les données dans
** des variables : le véritable envoie vers le LMS se fera dans myLMSFinish()
**
** Le Flash envoi toutes les commandes au format de tracking SCORM 1.2
** la fonction doLMSSetValueSCORM13 permet de convertir l'envoi au format SCORM 1.3
** Et pour le cas de l'HACP, les données sont juste stockées dans des variables
** en vue d'être envoyées à la fin de la session, à la fermeture du contenu.
**
*******************************************************************************/
function myLMSSetValue(name, value) {
	var buffer = false;

	if (value == undefined || value == "undefined") return;

	var arrayOfComponents = name.split(".");
	//Convertisseur des anciens termes de tracking
	if (arrayOfComponents[0] != "cmi") {
		switch (name){
			case "score": name = "cmi.core.score.raw"; break;
			case "session_time": name = "cmi.core.session_time"; break;
			case "lesson_status": name = "cmi.core.lesson_status"; break;
			case "lesson_location": name = "cmi.core.lesson_location"; break;
			case "suspend_data": name = "cmi.suspend_data"; break;
			case "author_data": name = "cmi.core.author_data"; break;
		}
		
		//On refait le split puisque le name a changé
		arrayOfComponents = name.split(".");
	}

	//Stocke l'intéraction
	tracking[name] = value;
		
	if (!_bOneShotTracking && _sTrackingMode != "HACP") {
		//Toutes les commandes (hors cas spécial des intéractions)
		//sont envoyées par le flash au format de tracking SCORM 1.2
		var sComm = String(arrayOfComponents.shift()); //Type de communication

		switch (sComm) {
			case "cmi":
				var sDataType = arrayOfComponents.shift() ; //Type de donnée "interactions" ou "theme".
				var sID = arrayOfComponents.shift(); //identifiant de l'enregistrement
				var sDataName = arrayOfComponents.join("."); //nom du champs de la donnée (on reconstitue éventuellement la chaine);

				//On va garder ces différentes infos sur l'élément.
				switch (sDataType) {
					/////////////////////////////////////////////////////////////////////
					// Cas des interactions
					/////////////////////////////////////////////////////////////////////
					case "interactions":
						switch (sDataName) {
							case "id":
							case "type":
							case "result":
							case "weighting":
							case "latency":
							case "description":
								//Cas des données communes
								addInteractionsInfo(sDataType, sID, sDataName, value); //Ajoute aux interactions HACP
								
								//puis continue comme pour les données plus spécifiques (pas de break !)

							case "correct_responses":		//Commun à SCORM 2004 et SCORM 1.2
							case "correct_response_text":	//Spécifique e-doceo
							case "student_response_text":	//Spécifique e-doceo
								switch (_sTrackingMode) {
									case "SCORM 1.2": doLMSSetValueSCORM12("cmi.interactions." + sID + "." + sDataName, value, true); break;
									case "SCORM 1.3": doLMSSetValueSCORM13("cmi.interactions." + sID + "." + sDataName, value, true); break;
								}
								break;

							case "student_response": //Uniquement SCORM 1.2
								if (_sTrackingMode == "SCORM 1.2") doLMSSetValueSCORM12("cmi.interactions." + sID + "." + sDataName, value, true);
								break;

							case "learner_response": //Uniquement SCORM 2004
								if (_sTrackingMode == "SCORM 1.3") doLMSSetValueSCORM13("cmi.interactions." + sID + "." + sDataName, value, true);
								break;
						}
						buffer = true;
						break;

					/////////////////////////////////////////////////////////////////////
					// Cas de l'agrégation du tracking par plages : spécifique e-doceo
					/////////////////////////////////////////////////////////////////////
					case "theme":
						addInteractionsInfo(sDataType, sID, sDataName, value); //Ajoute aux interactions HACP
						switch (_sTrackingMode) {
							case "SCORM 1.2": doLMSSetValueSCORM12("cmi.interactions." + sID + "." + sDataName, value, true); break;
							case "SCORM 1.3": doLMSSetValueSCORM13("cmi.interactions." + sID + "." + sDataName, value, true); break;
						}
						buffer = true;
						break;

					/////////////////////////////////////////////////////////////////////
					// Tous les autres cas
					/////////////////////////////////////////////////////////////////////
					default:
						switch (_sTrackingMode) {
							case "SCORM 1.2": doLMSSetValueSCORM12(name, value, false); break;
							case "SCORM 1.3": doLMSSetValueSCORM13(name, value, false); break;
						}
						buffer = true;
						break;
				}
				
				break; //end case "cmi"


			case "adl": //cas du adl.nav.request
				switch (_sTrackingMode) {
					case "SCORM 1.2": doLMSSetValueSCORM12(name, value, false); break;
					case "SCORM 1.3": doLMSSetValueSCORM13(name, value, false); break;
				}

				buffer = true;
				break; //end case "adl"
		}
	}

	//renvoi au flash le dernier code d'erreur
	setFlashVariable("myLMSGetLastErrorReturn", myLMSGetLastError()); 

	return buffer;
}

function myLMSCommit() {
	var buffer = "";

	if (!_bOneShotTracking) {
		switch (_sTrackingMode) {
			case "SCORM 1.2": buffer = doLMSCommitSCORM12(); break;
			case "SCORM 1.3": buffer = doLMSCommitSCORM13(); break;
		}
	}

	return buffer;
}

function myLMSGetValue(sElement) {
	var buffer = "";

	switch (_sTrackingMode) {
		case "SCORM 1.2": buffer = doLMSGetValueSCORM12(sElement); break;
		case "SCORM 1.3": buffer = doLMSGetValueSCORM13(sElement); break;
	}

	setFlashVariable("myLMSGetLastErrorReturn", myLMSGetLastError()); 
	setFlashVariable("myLMSGetValueReturn", buffer); 

	return buffer;
}

function myLMSGetLastError() {
	var buffer = true;

	switch (_sTrackingMode) {
		case "SCORM 1.2": buffer = doLMSGetLastErrorSCORM12(); break;
		case "SCORM 1.3": buffer = doLMSGetLastErrorSCORM13(); break;
		//case "HACP": buffer = doLMSGetLastErrorHACP(); break;
	}

	return buffer;
}


/**
* Méthode relative à la sauvegarde et mise en forme des interaction et données des plages Elm en HACP
* @sDataType : String - type d'information : "interactions", "theme"
* @sId : String - Référence de l'intéraction dans le tableau associatif général
* @sDataName : String - Nom de champs de l'interaction
* @sValue : String - Valeur de l'interaction
*/

function addInteractionsInfo(sDataType, sId, sDataName, sValue){
	//Si on cherche à stocker des données d'interaction simple.
	if (sDataType == "interactions") {
		
		//On va renommer des données pour coller avec le formatage final.
		if (sDataName == "id") sDataName = "interaction_id";
		if (sDataName == "theme") sDataName = "id_theme";
	
		//Si cet id d'interaction n'existe pas encore :
		if (aInteractionsList[sId] == undefined) aInteractionsList[sId] = initArray(aInteractionsList[sId], aInteractionHeaders.length);
		
		//On stocke l'info au bon endroit : recherche l'index du champ correspondant
		//var nIndex = getInteractionHeaderIndex(sDataName);
		var nIndex = getStringIndex(aInteractionHeaders, sDataName);
		aInteractionsList[sId][nIndex] = unescape(sValue);
			
	//Sinon, si on cherche à stocker des données de thème	
	} else if (sDataType == "theme") {
		//Si cet id de thème n'existe pas encore :
		if (aThemesList[sId] == undefined) {
			aThemesList[sId] = initArray(aThemesList[sId], aThemeHeaders.length);
		}

		//On stocke l'info au bon endroit : recherche l'index du champ correspondant
		//var nIndex = getThemesHeaderIndex(sDataName);
		var nIndex = getStringIndex(aThemeHeaders, sDataName);
		aThemesList[sId][nIndex] = unescape(sValue);
	}
}

/**
* getStringIndex
* @aTab : Array - Tableau dans lequel on doit rechercher la chaine
* @sName : String - Chaine recherchée
*
* @return : Number - renvoi l'index de la chaine recherchée dans le tableau
*/
function getStringIndex(aTab, sName){
	var buffer = -1;
	
	var nInteracs = aTab.length;
	for (var i = 0; i<nInteracs; i++) {
		var sTempName = aTab[i];
		if(sTempName == sName) buffer = i;
	}
	
	return buffer;
}

/**
* créé un tableau de la taille spécifiée, prérempli avec des chaines vides
* @nSize : Number - la taille du tableau
*
* @return : le nouveau tableau
*/
function initArray(nSize){
	var buffer = new Array();
	for (var i = 0; i<nSize; i++) {
		buffer.push('""');
	}
	return buffer;
}


/*******************************************************************************
**
** Gestion des relations avec l'objet Flash
**
*******************************************************************************/

/**
* Evénement déclenché à la réception d'une commande envoyée par l'objet Flash
*/
function DoFSCommand(command, args) {
	var buffer = false;

	args = String(args);
	command = String(command);
	var aArgs = args.split(";");
	
	switch (command) {
		case "LMSInitialize": buffer = myLMSInitialize(); break;
		case "LMSSetValue": 
				arg0 = aArgs.shift();
				arg1 = aArgs.join(";");
				buffer = myLMSSetValue(arg0, arg1);
				//buffer = myLMSSetValue(aArgs[0], aArgs[1]); 
				break;
		case "LMSGetValue": buffer = myLMSGetValue(aArgs[0]); break;
		case "LMSCommit": buffer = myLMSCommit(); break;
		case "OnOpenDocument": window.open(aArgs[0], 'edoceo_document',''); break;
		case "OnGetSuspendDataBlock": sendSuspendDataToPlayer(aArgs[0]); break;

		case "LMSFinish":
		case "sendQuit": 
			buffer = myLMSFinish();
			if (window.top.opener != null) window.top.close();
			break;
	}
}

/**
* Créé dynamiquement l'objet Flash
*/
function writeFlashPlayer(sFileName, sParameters, sBgColor, sWidth, sHeight, sOneShotTracking) {
	_bOneShotTracking = (sOneShotTracking == "1");

	// cadrage
	document.writeln('<table width="100%" height="100%" cellspacing="0" cellpadding="0"><tr><td valign="middle" align="center">');
	// IExplorer
	document.writeln('<OBJECT classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0" ID="player" width="' + sWidth + '" height="' + sHeight + '">');
	document.writeln('<param name="allowScriptAccess" value="sameDomain">');
	document.writeln('<param name="movie" value="' + sFileName +  '?' + sParameters + ' ">');
	document.writeln('<param name="quality" value="high">');
	document.writeln('<param name="bgcolor" value="' + sBgColor + '">');		
	// Netscape
	document.writeln('<EMBED SRC="' + sFileName + '?' + sParameters + '" quality="high" bgcolor="' + sBgColor + '" width="' + sWidth + '" height="' + sHeight + '" name="player" align="middle" swLiveConnect="true" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer">');
	document.writeln('</EMBED></OBJECT>');
	document.writeln('</td></tr></table>');
}


/**
* Envoi une donnée à l'objet Flash
*/
function setFlashVariable(sVarName, sValue) {
	var movie = getFlashMovieObject("player");
	if (movie != null) movie.SetVariable(sVarName, sValue);
}

/**
* Retourne une référence à l'objet Flash
*/
function getFlashMovieObject(movieName) {
	if (window.document[movieName]) return window.document[movieName];
  
	if (navigator.appName.indexOf("Microsoft Internet")==-1) {
		if (document.embeds && document.embeds[movieName]) return document.embeds[movieName]; 
	} else {
		return document.getElementById(movieName);
	}
}

/**
* on découpe le suspend_data en bloc de NB_MAX_SIZE_SUSPEND_DATA caractères pour contourner la limitation de IE
* @param	sIndex
*/
function sendSuspendDataToPlayer(sIndex) {
	var sTrackingDataRef = _aDataBlockRefs[_iDataBlockRefIndex];
	var sTrackingData = new String(tracking[sTrackingDataRef]);

	var nL = sTrackingData.length;
	var n = Math.floor((nL - 1) / NB_MAX_SIZE_SUSPEND_DATA) + 1;
	var nIndex = Number(sIndex);
	if (nIndex >= 0 && nIndex < n) {
		// on envoie le bloc nIndex
		setFlashVariable("sSuspendData", sTrackingData.substr(nIndex * NB_MAX_SIZE_SUSPEND_DATA, NB_MAX_SIZE_SUSPEND_DATA)); 
	} else {
		if (_iDataBlockRefIndex < _aDataBlockRefs.length - 1) {
			_iDataBlockRefIndex++;
			// on signale le début de l'envoi d'une nouvelle variable
			setFlashVariable("sSuspendData", _aDataBlockRefs[_iDataBlockRefIndex]); 
		} else {
			// on signale la fin de l'envoi
			setFlashVariable("sSuspendData", "stop"); 
		}
	}
}

/**
* Vérifie la présence de sValue dans aArray
* @param	aArray
* @param	sValue
* @return	Un booléen indiquant si sValue se trouve dans aArray
*/
function isValueInArray(aArray, sValue) {
	var buffer = false;
	var i = 0;
	var n = aArray.length;
	while(i < n && !buffer) {
		buffer = (aArray[i] == sValue);
		i++;
	}
	return buffer; 
}

/**
* Renvoi le nom de champs d'une chaine du type cmi.interactions.n.id
* @param	Une chaine indiquant le chemin complet du champs de données
* @return	Une chaine ayant pour valeur le dernier élément de la chaine passée en paramètres
*/
function getLastFieldName(sElement) {
	var aList = sElement.split(".");
	return aList[aList.length - 1]; 
}

/**
* renvoi l'identifiant de l'interaction
* @param	sElement
*/
function getInteractionName(sElement) {
	var aList = sElement.split(".");
	return aList[aList.length - 1]; 
}

/**
* essaie d'envoyer la fermeture de LMS quand on clique sur la croix pour fermer
*/
function tryToLMSFinish() {
	setFlashVariable("tryToLMSFinish", "");
	return false;
}


/**
*
*  UTF-8 data encode / decode
*  http://www.webtoolkit.info/
*
**/

var Utf8 = {

    // public method for url encoding
    encode : function (string) {
        string = string.replace(/\r\n/g,"\n");
        var utftext = "";

        for (var n = 0; n < string.length; n++) {

            var c = string.charCodeAt(n);

            if (c < 128) {
                utftext += String.fromCharCode(c);
            }
            else if((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }

        }

        return utftext;
    },

    // public method for url decoding
    decode : function (utftext) {
        var string = "";
        var i = 0;
        var c = c1 = c2 = 0;

        while ( i < utftext.length ) {

            c = utftext.charCodeAt(i);

            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            }
            else if((c > 191) && (c < 224)) {
                c2 = utftext.charCodeAt(i+1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            }
            else {
                c2 = utftext.charCodeAt(i+1);
                c3 = utftext.charCodeAt(i+2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }

        }

        return string;
    }

}
