/**
 * Ecran de chargement d'un niveau / d'un circuit
 * 
 * S'affiche, lance le chargement du circuit et de ses images
 * et termine une fois le chargement achevé sans intervention utilisateur
 *
 */
 
 
 /**
  * Création de l'écran de chargement
  *  - application : l'application (RacingGameApp) à qui rendre la main à l'issue
  *  - container : l'objet DIV qui contiendra les éléments DHTML créés
  *  - season : la saison décrivant les différents circuits
  *  - fader : le fader sur le container (état Out, i.e. masqué, en entrée)
  */
 function RaceLoader(application, engine, container, season, fader)
 {
	this.application = application;
	this.engine = engine;
	this.container = container;
	this.season = season;
	this.fader = fader;
	this.imageFx = engine.imageFx;
		
	this.sourceFiles = new Array();
	this.progressBarWidth = 460;	// nombre de pixels de la barre de progression
	this.ajaxProgressWidth = 40;	// parmi les précédents, largeur réservée pour le chargement Ajax
 }
 
 RaceLoader.prototype = {
   
	/**
	 * Création et initialisation des éléments d'affichage (DHTML)
	 *
	 * Prérequis : le fader est Out en entrée, l'appel lance le bascule en In
	 */
	launchScreen : function(modeIndex, raceIndex, carIndex, playerCarImage, canQualify) {

		// réinitialisation des variables de contexte
		this.requestedCount = 0; // nombre dont le chargement a été demandé
		this.loadedCount = 0; // nombre dont le chargement est terminé
		this.totalCount = 0; // nombre total à charger
		this.errorCount = 0; // nombre dont le chargement a causé une erreur
		this.loadAborted = false; // interruption du fait de l'utilisateur
		this.loadFailed = false; // interruption suite à une erreur non récupérable
		this.userAcknowledged = false; // acquittement utilisateur sur un message d'erreur
		this.ajaxProgress = 0; // de 0 à 4, suivant le modèle XMLHttpRequest (pour la barre de progression)
		this.canQualify = canQualify; // true pour afficher le temps de qualification
		this.timer = 0;
		this.loadCompleteTimer = 0;
	
		// variables conservées car elles seront utilisées pour remplir
		// le tableau sourceFiles[] .. mais seulement après complétion
		// de l'appel Ajax destiné à récupérer le circuit, donc de
		// manière asynchrone
		this.playerCarImage = playerCarImage;
		this.raceDirectory =  this.season.getTrackFolder(raceIndex);
		
		this.splashImage = document.createElement('img');
		this.splashImage.setAttribute("class", "loader_splash");
		this.splashImage.setAttribute("src", this.raceDirectory+"splash.png");
		this.container.appendChild(this.splashImage);
		
		this.topBar = this.imageFx.createOutlinedTextPanel(this.container, "loader_top_bar", "loader_text", "white");
		var announcement = "Route 101";
		this.imageFx.outlinedWrite(this.topBar, announcement);
		
		this.bottomBar = this.imageFx.createOutlinedTextPanel(this.container, "loader_bottom_bar", "loader_text", "white");

		this.messagePanel = this.imageFx.createOutlinedTextPanel(this.container, "loader_center_message", "loader_text", "red");
		

		
		this.topRibbon = document.createElement('div');
		this.topRibbon.setAttribute("class", "loader_black_ribbon");
		this.topBar.appendChild(this.topRibbon);
		
		this.bottomRibbon = document.createElement('div');
		this.bottomRibbon.setAttribute("class", "loader_black_ribbon");
		this.bottomBar.appendChild(this.bottomRibbon);
		
		this.progressContainer = document.createElement('div');
		this.progressContainer.setAttribute("class", "loader_progress_container");
		this.container.appendChild(this.progressContainer);

		this.progressBar = document.createElement('div');
		this.progressBar.setAttribute("class", "loader_progress_bar");
		this.progressContainer.appendChild(this.progressBar);

		this.progressCover = document.createElement('div');
		this.progressCover.setAttribute("class", "loader_progress_cover");
		this.progressBar.appendChild(this.progressCover);

		this.completedTextArea = this.imageFx.createOutlinedTextPanel(this.progressContainer, "loader_completed_message", "loader_text", "white");
		
		this.fader.fadeIn();
		
		// inclut l'appel Ajax pour obtenir le circuit auprès du serveur
		// les cas d'erreur sont traités par la callback extractImages()
		this.engine.road = new Road();
		this.engine.road.createRoad(modeIndex, raceIndex, carIndex, this.raceDirectory, this);
		
		var control = this;
		document.onkeydown = function(event) { control.onKeyDown(event); }
		
		this.mainLoop();
	},

	/**
	 * Initie le chargement des images liées au circuit (instance de Road) juste récupéré.
	 * Si le circuit est invalide, sort sur une erreur.
	 * Sinon remplit le tableau sourceFiles[] des images à charger
	 * et définit la variable this.totalCount, afin que la mainLoop() initie le chargement
	 */
	extractImages : function() {
	
		if (this.engine.road.invalidRoad) {
			this.loadFailed = true;
			this.userAcknowledged = false;
			this.imageFx.outlinedWrite(this.messagePanel, _("LO002"));
			this.totalCount = 0;
		} else {
			// affichage des temps
			var recordMessage = "released for demojs 2012.";
			if (this.engine.road.lapRecords[0].time > 0) {
				var recordMessage = _("LO004", this.formatRaceTime(this.engine.road.lapRecords[0].time));
			}
			if (this.engine.road.playerLapRecord.time > 0) {
				recordMessage+=_("LO005", this.formatRaceTime(this.engine.road.playerLapRecord.time));
			}
			if (this.canQualify) { 
				// on n'affiche le temps de qualification que si le joueur n'a pas encore
				// réussi à accéder au niveau suivant
				if (this.engine.road.lapRecords[0].time > 0) {
					recordMessage+=", ";
				}
				recordMessage+=_("LO006", this.formatRaceTime(this.engine.road.qualifyingTime));
			}
			this.imageFx.outlinedWrite(this.bottomBar, recordMessage);
			
			// Regroupement des images à charger dans le tableau sourceFiles[]
			var index=0;
			this.sourceFiles[0] = this.playerCarImage;
			this.sourceFiles[1] = "cars/protonSprite.png";
			this.sourceFiles[2] = this.raceDirectory+"roadline_g.png";
			var offset = 3;
			for (index=0; index<this.engine.road.sceneryTypeCount; ++index) {
				this.sourceFiles[offset+index] = this.engine.road.sceneryMaster[index].src;
			}
			offset += this.engine.road.sceneryTypeCount;
			this.sourceFiles[offset] = this.engine.road.horizon.src;
			if (this.engine.road.horizon.reflection) {
				this.sourceFiles[++offset] = this.engine.road.horizon.reflection;
			}
			this.totalCount = offset+1;
			
		}
	},
	
	/**
	 * Chargement effectif des images.
	 * Le principe de la mainLoop est d'effectuer 2 chargements par appel à la méthode
	 * (1 fois par 20 ms soit 2*50 images par seconde). Ce mécanisme non-blocant permet
	 * de continuer à mettre à jour l'affichage à chaque appel.
	 * On compte 300 images par course soit 3 secondes de chargement (minimum).
     *
	 * Le mécanisme initial prévoyait de lancer le chargement de toutes les images
	 * via une boucle blocante, puis d'attendre les notifications de chargement (ou 
	 * d'erreur) pour mettre à jour la barre de progression.
	 * L'inconvénient est que Javascript ne gérant apparemment pas le multithreading,
	 * cela bloquait le fader, ainsi que la réception des premiers événements de chargement
	 * qui n'étaient traités qu'une fois la boucle de chargement complétée.
	 */
	mainLoop : function() {
	
		// chargement des images suivantes
		var next2 = this.requestedCount+2;
		for (;this.requestedCount < this.totalCount && this.requestedCount < next2 ; ++this.requestedCount)
		{
			var currentImage = new Image;
			currentImage.onload = function() {++this.loader.loadedCount;} ;
			currentImage.onerror = function() {++this.loader.errorCount;} ;
			currentImage.onabort = function() {this.loader.loadAborted = true;}
			currentImage.loader = this;
			currentImage.src = this.sourceFiles[this.requestedCount];
		}
		
		// traitement des erreurs
		if (!this.loadFailed && this.errorCount>2) {
			this.loadFailed = true;
			this.imageFx.outlinedWrite(this.messagePanel, _("LO003"));
			this.userAcknowledged = false;
		}
	
		var loadCompleted = this.totalCount > 0 && this.requestedCount == this.totalCount 
							&& (this.loadedCount+this.errorCount == this.requestedCount);

		// défilement de la barre de progression (largeur = 460 pixels)
		// 40 pixels pour l'appel Ajax, les 420 autres pour le chargement des images
		++this.timer;
		this.progressBar.style.backgroundPosition=(5*this.timer)+"px 0px";
		var progress = Math.round(0.01*this.ajaxProgressWidth*this.ajaxProgress); // ajaxProgress de 0 à 100
		if (this.totalCount > 0) {
			var imageProgressWidth = this.progressBarWidth-this.ajaxProgressWidth;
			var progress = this.ajaxProgressWidth+Math.round(imageProgressWidth*this.loadedCount/this.totalCount);
		}
		this.progressCover.style.left=progress+"px";
		this.progressCover.style.width=(this.progressBarWidth-progress)+"px";
	
		if (loadCompleted) {
			if (this.loadCompleteTimer == 0) {
				this.loadCompleteTimer = this.timer;
				this.imageFx.outlinedWrite(this.completedTextArea, _("LO007"));
			}
			var elapsedTime = this.timer-this.loadCompleteTimer;
			
			if (elapsedTime<=20) {
				this.completedTextArea.style.opacity=(elapsedTime/20);
				this.progressBar.style.top=elapsedTime+"px";
				this.progressBar.style.clip="rect(0px, 460px, "+(20-elapsedTime)+"px, 0px)";
			}
			var highlightColor="rgb("+Math.round(127+127*Math.sin(elapsedTime/20))+","+Math.round(127-127*Math.sin(elapsedTime/20))+",255)";
			this.imageFx.setHighlightColor(this.completedTextArea, highlightColor);
		}
	
		var keepOn = true;
		if (this.loadAborted || ((this.loadFailed || loadCompleted )&& this.userAcknowledged) )
		{
			if (!this.fader.fading) { // le rideau est à l'arrêt
				if (this.fader.faded) { // soit il est déja tiré
					this.terminateScreen();
					keepOn = false;
				} else { // soit on est au tout début et on doit le tirer
					this.fader.fadeOut();
				}
			}
		} 
		
		if (keepOn) {
			setTimeout("raceLoaderMainLoop(lotus.raceLoader)", 20);
		}
	},
	
	/**
	 * Ferme l'écran de chargement, nettoie les objets HTML de l'affichage
	 * et rend la main au RacingGameApp
	 */
	terminateScreen : function() {
		document.onkeydown = null;
		this.progressBar.removeChild(this.progressCover);
		this.progressContainer.removeChild(this.progressBar);
		this.container.removeChild(this.progressContainer);
		this.container.removeChild(this.messagePanel);
		this.container.removeChild(this.bottomBar);
		this.container.removeChild(this.topBar);
		this.container.removeChild(this.splashImage);
		this.application.raceLoaded();
	},
	
	/**
	 * Formate un temps en 1/100e de seconde vers une chaîne "mm:ss:cc",
	 * en rajoutant si nécessaire un zéro devant les secondes ou 1/100e de seconde
	 * pour garder un format à deux chiffres.
	 */
	formatRaceTime : function(inTime) {
		inTime = parseInt(inTime);
		var timeString = "--:--:--";
		if (inTime > 0) {
			var cs = inTime%100;
			var seconds = ((inTime-cs)/100)%60;
			var minutes = Math.floor(inTime/6000);
			timeString = (minutes<10?"0":"")+minutes+":"+(seconds<10?"0":"")+seconds+":"+(cs<10?"0":"")+cs;
		}
		return timeString;
	},
	
	/**
	 * Handler des événements onkeydown (touche clavier pressée)
	 * Acquittement d'un message sur espace ou entrée
	 */
	onKeyDown : function(event) {		
		var key = 0;
		if (window.event) { // IE
			key = window.event.keyCode;
		} else { // FF, Opera,...
			key = event.which;
		}
		this.userAcknowledged = this.userAcknowledged || key==13 || key==32;
	}

}
	
function raceLoaderMainLoop(screen)
{
	screen.mainLoop();
}	