/**
 * La classe Garage sert à stocker les différents modèles de voitures
 * disponibles dans le jeu (joueur et adversaires)
 */
 
 /**
  * Crée une instance d'un Garage et alloue le nombre d'emplacements pour
  * les voitures. Seule une partie sera effectivement accessibles au joueur,
  * le reste étant réservé pour les adversaires.
  * Les voitures ne sont pas créées.
  *
  *  - playerLimit : nombre de voitures (les n premières) pour le joueur
  *  - size : taille totale du garage (nombre de voitures)
  */
function Garage(playerLimit, size)
{
	this.playerCarCount = playerLimit;
	this.carCount = size;
	this.cars = new Array(this.carCount);
}
 
Garage.prototype = {

	/**
	 * Peuple le garage avec les différentes voitures du jeu, et définit celles qui sont accessibles au joueur
	 *
	 * La description d'une voiture comporte :
	 *  - name : son nom, tel qu'affiché dans l'écran de sélection
	 *  - clueText : l'indice donné si la voiture est verrouillée, afin de la chercher dans le jeu
	 *  - gearRatio : rapports de la boîte de vitesses
	 *  - topGear : nombre de vitesses dans la boîte (taille de gearRatio - 1)
	 *  - maxTorque : couple maxi, en N.m
	 *  - torqueLoss : perte de couple, le couple est approximé par une parabole, 
	 *    la formule est maxTorque-torqueLoss*(r-torquePeak)²
	 *  - torquePeak : régime moteur correspondant au couple maxi (tours/mn)
	 *  - weight : masse de la voiture en kg
	 *  - pixelWidth : largeur pour les tests de collision, en pixels
	 *  - length : longueur pour les tests de collision, en cm
	 *  - imageFile : chemin d'accès vers l'image du sprite
	 *  - selectionImage : chemin d'accès vers l'image descriptive sur l'écran de choix de la voiture
	 */
	defineCars : function(lockMask)
	{
		this.cars[0] = {
			name: "Cascade",
			clueText: _("GA001"),
			gearRatio: [9999, 4.23, 2.12, 1.52, 1.10, 0.92],
			topGear: 5,
			maxTorque: 190,
			torqueLoss: 0.000013,
			torquePeak: 3800,
			skidFactor: 0.25,
			weight : 890,	// en kg
			pixelWidth: 63, // 63 pixels de large pour les collisions
			length: 400, // 400cm = 4m de long pour les collisions
			imageFile: "cars/cascadeSprite.png",
			selectionImage : "cars/cascadeDetails.png",
			drive : 'F',
			locked: false };
		this.cars[1] = {
			name: "Talisman",
			clueText: _("GA002"),
			gearRatio: [9999, 3.85, 2.65, 1.67, 1.05, 0.85],
			topGear: 5,
			maxTorque: 220,
			torqueLoss: 0.000013,
			torquePeak: 3900,
			skidFactor: 0.38,
			weight : 1365,
			pixelWidth: 64, // 64 pixels de large pour les collisions
			length: 474, // 474cm = 4m74 de long pour les collisions
			imageFile: "cars/talismanSprite.png",
			selectionImage : "cars/talismanDetails.png",
			drive : 'F',
			locked: false };
		this.cars[2] = {
			name: "Avalanche",
			clueText: _("GA003"),
			gearRatio: [9999, 2.07, 1.25, 1.08, 0.90, 0.57],
			topGear: 5,
			maxTorque: 360,
			torqueLoss: 0.000018,
			torquePeak: 1950,
			skidFactor: 0.42,
			weight : 1385,
			pixelWidth: 64, // 64 pixels de large pour les collisions
			length: 466, // 466cm = 4m66 de long pour les collisions
			imageFile: "cars/avalancheSprite.png",
			selectionImage : "cars/avalancheDetails.png",
			drive : 'R',
			locked: false };
		this.cars[3] = {
			name: "Rhino",
			clueText: _("GA004"),
			gearRatio: [9999, 3.77, 2.32, 1.52, 1.15, 0.86],
			topGear: 5,
			maxTorque: 443,
			torqueLoss: 0.000018,
			torquePeak: 3100,
			skidFactor: 0.28,
			weight : 1885,
			pixelWidth: 64, // 64 pixels de large pour les collisions
			length: 487, // 487cm = 4m87 de long pour les collisions
			imageFile: "cars/rhinoSprite.png",
			selectionImage : "cars/rhinoDetails.png",
			drive : '4',
			locked: false };
		this.cars[4] = {
			name: "Torrent",
			clueText: _("GA005"),
			gearRatio: [9999, 3.61, 2.25, 1.67, 1.26, 0.86, 0.63],
			topGear: 6,
			maxTorque: 230,
			torqueLoss: 0.000024,
			torquePeak: 3800,
			skidFactor: 0.3,
			weight : 1310,
			pixelWidth: 64, // 64 pixels de large pour les collisions
			length: 441, // 441cm = 4m41 de long pour les collisions
			imageFile: "cars/torrentSprite.png",
			selectionImage : "cars/torrentDetails.png",
			drive : 'F',
			locked: false };
		this.cars[5] = {
			name: "Trident",
			clueText: _("GA006"),
			gearRatio: [9999, 3.91, 2.15, 1.62, 1.15, 0.95, 0.79],
			topGear: 6,
			maxTorque: 330,
			torqueLoss: 0.0000228,
			torquePeak: 4200,
			skidFactor: 0.35,
			weight : 1610,
			pixelWidth: 64, // 64 pixels de large pour les collisions
			length: 462, // 462cm = 4m62 de long pour les collisions
			imageFile: "cars/tridentSprite.png",
			selectionImage : "cars/tridentDetails.png",
			drive : 'F',
			locked: false };
		this.cars[6] = {
			name: "Plasma",
			clueText: _("GA007"),
			gearRatio: [9999, 3.92, 1.96, 1.27, 1.00, 0.84, 0.68],
			maxTorque: 354,
			torqueLoss: 0.000025,
			torquePeak: 3900,
			skidFactor: 0.45,
			weight : 1280,
			topGear: 6,
			pixelWidth: 64, // 64 pixels de large pour les collisions
			length: 446, // 446cm = 4m46 de long pour les collisions
			imageFile: "cars/plasmaSprite.png",
			selectionImage : "cars/plasmaDetails.png",
			drive : 'R',
			locked: false };
		this.cars[7] = {
			name: "Silhouette",
			clueText: _("GA008"),
			gearRatio: [9999, 3.82, 2.07, 1.42, 1.11, 0.89, 0.70],
			topGear: 6,
			maxTorque: 320,
			torqueLoss: 0.000023,
			torquePeak: 3600,
			skidFactor: 0.33,
			weight : 1390,
			pixelWidth: 64, // 64 pixels de large pour les collisions
			length: 404, // 404cm = 4m04 de long pour les collisions
			imageFile: "cars/sprite_diablo.png",
			selectionImage : "cars/notAvail.png",
			drive : 'R',
			locked: false };
		this.cars[8] = {
			name: "Cyclone",
			clueText: _("GA009"),
			gearRatio: [9999, 3.82, 2.07, 1.42, 1.11, 0.89, 0.70],
			topGear: 6,
			maxTorque: 320,
			torqueLoss: 0.000023,
			torquePeak: 3600,
			skidFactor: 0.45,
			weight : 1390,
			pixelWidth: 64, // 64 pixels de large pour les collisions
			length: 404, // 404cm = 4m04 de long pour les collisions
			imageFile: "cars/sprite_diablo.png",
			selectionImage : "cars/notAvail.png",
			drive : 'R',
			locked: false };
		this.cars[9] = {
			name: "Silenz",
			clueText: _("GA010"),
			gearRatio: [9999, 2.31, 1.52, 1.12, 0.88, 0.66],
			topGear: 5,
			maxTorque: 165,
			torqueLoss: 0.000015,
			torquePeak: 3000,
			skidFactor: 0.45,
			weight : 1365,
			pixelWidth: 64, // 64 pixels de large pour les collisions
			length: 400, // 400cm = 4m de long pour les collisions
			imageFile: "cars/sprite_diablo.png",
			selectionImage : "cars/notAvail.png",
			drive : 'F',
			locked: true };
		this.cars[10] = {
			name: "Djinn",
			clueText: _("GA011"),
			gearRatio: [9999, 2.31, 1.52, 1.12, 0.88, 0.66],
			topGear: 5,
			maxTorque: 383,
			torqueLoss: 0.000015,
			torquePeak: 3500,
			skidFactor: 0.37,
			weight : 1400,
			pixelWidth: 64, // 64 pixels de large pour les collisions
			length: 445, // 445cm = 4m45 de long pour les collisions
			imageFile: "cars/djinnSprite.png",
			selectionImage : "cars/djinnDetails.png",
			drive : '4',
			locked: true };
		this.cars[11] = {
			name: "Jester",
			clueText: _("GA012"),
			gearRatio: [9999, 2.31, 1.52, 1.12, 0.88, 0.66],
			topGear: 5,
			maxTorque: 400,
			torqueLoss: 0.000015,
			torquePeak: 4000,
			skidFactor: 0.42,
			weight : 1520,
			pixelWidth: 64, // 64 pixels de large pour les collisions
			length: 400, // 400cm = 4m de long pour les collisions
			imageFile: "cars/sprite_diablo.png",
			selectionImage : "cars/notAvail.png",
			drive : 'R',
			locked: true };
		this.cars[12] = {
			name: "Vortex",
			clueText: _("GA013"),
			gearRatio: [9999, 2.31, 1.52, 1.12, 0.88, 0.66],
			topGear: 5,
			maxTorque: 480,
			torqueLoss: 0.000015,
			torquePeak: 6800,
			skidFactor: 0.45,
			weight : 1480,
			pixelWidth: 64, // 64 pixels de large pour les collisions
			length: 450, // 450cm = 4m50 de long pour les collisions
			imageFile: "cars/sprite_diablo.png",
			selectionImage : "cars/notAvail.png",
			drive : 'R',
			locked: true };
		this.cars[13] = {
			name: "Menace",
			clueText: _("GA014"),
			gearRatio: [9999, 2.31, 1.52, 1.12, 0.88, 0.66],
			topGear: 5,
			maxTorque: 510,
			torqueLoss: 0.000015,
			torquePeak: 4500,
			skidFactor: 0.45,
			weight : 1569,
			pixelWidth: 64, // 64 pixels de large pour les collisions
			length: 430, // 430cm = 4m30 de long pour les collisions
			imageFile: "cars/sprite_diablo.png",
			selectionImage : "cars/notAvail.png",
			drive : '4',
			locked: true };
		this.cars[14] = {
			name: "Doppler",
			clueText: _("GA015"),
			gearRatio: [9999, 2.31, 1.52, 1.12, 0.88, 0.66],
			topGear: 5,
			maxTorque: 660,
			torqueLoss: 0.000015,
			torquePeak: 6600,
			skidFactor: 0.45,
			weight : 1665,
			pixelWidth: 64, // 64 pixels de large pour les collisions
			length: 470, // 470cm = 4m70 de long pour les collisions
			imageFile: "cars/sprite_diablo.png",
			selectionImage : "cars/notAvail.png",
			drive : 'R',
			locked: true };
		this.cars[15] = {
			name: "Tachyon",
			clueText: _("GA016"),
			gearRatio: [9999, 2.31, 1.52, 1.12, 0.88, 0.66],
			topGear: 5,
			maxTorque: 949,
			torqueLoss: 0.000021,
			torquePeak: 4800,
			skidFactor: 0.45,
			weight : 1247,	// en kg
			pixelWidth: 64, // 64 pixels de large pour les collisions
			length: 477, // 477cm = 4m77 de long pour les collisions
			imageFile: "cars/sprite_impreza_bleue.png",
			selectionImage : "cars/notAvail.png",
			drive : 'R',
			locked: false };
		
		var mask = 1;
		for (var index = 0; index < this.carCount; ++index)
		{
			this.cars[index].locked = ((lockMask & mask) == 0);
			mask = mask << 1;
		}
		this.precomputeShiftingSpeeds();
	},
	
	/**
	 * Calcule et stocke, pour chaque voiture, pour chaque rapport de boîte,
	 * la vitesse idéale de passage du rapport supérieur. Il sera utilisé dans
	 * l'asservissement de la boîte automatique pour décider d'un changement de
	 * rapport.
	 *
	 * La vitesse idéale est calculée comme le point où l'accélération du rapport
	 * supérieur rejoint celui du rapport courant.
	 * La formule est une équation du 2nd degré :
	 * g.(r+k.T(r)/g²) = h.(s+k.T(s)/h²) avec
	 *  - g coefficient de réduction du rapport courant
	 *  - h coefficient de réduction du rapport supérieur
	 *  - T couple moteur (= a - b.(r-c)²)
	 *  - k coefficient relatif à la transmission
	 *  - r régime moteur (la variable à résoudre)
	 *  - s régime correspondant sur le rapport supérieur (=h.r/g)
	 * La vitesse de la voiture rentre ultérieurement dans l'équation pour simplifier
	 */
	precomputeShiftingSpeeds : function()
	{
		var engineFriction = 0;
		for (var carIndex=0; carIndex<this.carCount; ++carIndex)
		{
			var gearCount = this.cars[carIndex].topGear;
			this.cars[carIndex].shiftingSpeed = new Array(gearCount+1);
			this.cars[carIndex].shiftingSpeed[gearCount]=99999;
			// on ne considère les rapports que de 1 à max-1, le point mort ne nous
			// intéresse pas (la vitesser est 0), et le rapport maxi non plus (on ne
			// passe jamais la supérieure)

			var maxTorque = this.cars[carIndex].maxTorque;
			var torqueLoss = this.cars[carIndex].torqueLoss;
			var torquePeak = this.cars[carIndex].torquePeak;

			for (var gear=1; gear<gearCount; ++gear)
			{
				var g = 1/(this.cars[carIndex].gearRatio[gear]*150.0);
				var h = 1/(this.cars[carIndex].gearRatio[gear+1]*150.0);
								
				var aCoef = (torqueLoss+engineFriction)*(g*g+g*h+h*h);
				var bCoef = -2*torqueLoss*torquePeak*g*h*(g+h);
				var cCoef = g*g*h*h*(torqueLoss*torquePeak*torquePeak-maxTorque);
				var delta = bCoef*bCoef-4*aCoef*cCoef;
				
				var speed =  (Math.sqrt(delta)-bCoef)/(2*aCoef);
				this.cars[carIndex].shiftingSpeed[gear]=speed;
			}
		}
	},
	
	
	/**
	 * Renvoie par référence (tableau) la description d'une voiture.
	 * Si la valeur n'est pas dans l'intervalle, renvoie null. Attention, l'index est
	 * de base 1 (1 pour la 1re voiture, pas 0)
	 */
	getCar : function(index) {
		if (index>=1&&index<=this.carCount) {
			return this.cars[index-1];
		}
		return null;
	},
	
		
	
	
	/**
	 * Déverrouille une voiture, la rendant ainsi accessible au joueur lors
	 * de sa prochaine partie.
	 * Attention, l'index est de base 1 (1 pour la 1re voiture, pas 0)
	 *
	 * La base SQL n'est pas mise à jour par cet appel !
	 */
	unlockCar : function(index)
	{
		if (index>=1&&index<=this.carCount) {
			this.cars[index-1].locked=false;
		}
	}
}