// FILE		data_access.js
// DATE		2005/08/19 19:35:55
//
//
// DESCRIPTION
//	Ecmascript configurator's data access functions.


/* Indique si une donnée se trouve dans un tableau
 *
 * @param	Array(n)	tableau à vérifier
 * @param	?		valeur à chercher
 *
 * @return	bool		présence de la donnée à chercher
 */
function in_array(array, data)
{
	// vérification spirituelle, ligne par ligne...
	for ( var key in array )
	{
		var ligne = array[key];
		if ( typeof(ligne) == "object" )
		{
			// no built-in identity operator for arrays in javascript...
			// lets roll the second level of the array
			var equality = true;
			for ( var intermediate_key in ligne )
			{
				equality = equality && (ligne[intermediate_key] == data[intermediate_key]);
			}
			if ( equality )
			{
				return true;
			}
		}
		else
		{
			if ( ligne == data )
			{
				return true;
			}
		}
	}
	return false;
}


/* Effectue la projection d'un tableau sur un vecteur
 *
 * @param	Array(n)	tableau à projeter
 * @param	Array(1)	vecteur de projection
 */
function projection(myArray, vector)
{
	var i = 0;
	while ( i < myArray.length )
	{
		var tuple = new Array();
		for ( var key in vector )
		{
			var label = vector[key];
			tuple[label] = myArray[i][label];
		}
		myArray[i] = tuple;
		i++;
	}
}


// Comparaison de caractère
function compare_caract(car1,car2)
{
	var tnb1 = car1[global_column];
	var tnb2 = car2[global_column];	
	var nb1 = tnb1.replace(",",".");
	var nb2 = tnb2.replace(",",".");
	var result = nb1 - nb2;
	return result;
}

var global_column;
/* Trie un tableau multi-dimensionnel suivant une de ses colonnes, 
 * à l'aide l'algorithme quicksort. (ordre ascendant)
 *
 * @param	Array(n)	tableau à trier
 * @param	int		borne inférieure de la zone du tableau sur 
 *				laquelle doit porter le tri.
 * @param	int		borne supérieure de la zone du tableau sur 
 *				laquelle doit porter le tri.
 * @param	string		colonne de support du tri.
 */
function quicksort(myArray, inf, sup, column)
{
	global_column = column;
	
	if (sup < inf || myArray.length <= 1)
	{
		return;
	}		
	
	
	var i;
	var j = 0;
	var tabtmp = new Array();
	
	if (inf == 0 && sup == myArray.length-1)
	{
		// Tri effectué selon la colonne sélectionnée
		if (column == "price" || column == "car1" || column == "car2" || column == "car3" || column == "promo")
		{
			myArray = myArray.sort(compare_caract);
		}
		else
		{
			myArray = myArray.sort();
		}
		
	}
	else
	{
		// Extraction lignes
		for (i = inf; i <= sup ; i++)
		{
			tabtmp[j] = myArray[i];
			j++;
		}
		
		// Tri des valeurs spécifiées selon les bornes
		if (column == "price" || column == "car1" || column == "car2" || column == "car3" || column == "promo")
		{
			tabtmp = tabtmp.sort(compare_caract);
		}
		else
		{
			tabtmp = tabtmp.sort();			
		}		
		
		// Reconstruction du tableau d'origine	
		j = inf;
		for (i = 0; i < tabtmp.length; i++)
		{
			myArray[j] = tabtmp[i];
			j++;
		}
	}
	
}


/* Valide un tuple par rapport à certaines conditions. Ces conditions sont 
 * exprimées sous la forme d'un tableau qui représente une structure d'arbre :
 *
 * condition = { operateur_d_association  cond_1 cond_2 [cond_3] ... [cond_n] }
 * ou :
 * condition = { operateur_de _comparaison  propriété valeur }
 *
 * exemple :
 * Soit C = ( (model == M4) && ((grBodyStyle == S2) || (grBodyStyle== S3)) 
 *		&& (price >= 12000) && (price <= 19500) )
 *
 * Ecrit en mode infixe,
 * C = ( && (== model 1M4)		Cet exemple validera uniquement les 
 *          (|| (== grBodyStyle S2)	versions du modèle 1M4 en silhouettes 
 *              (== grBodyStyle S3))	S2 ou S3, et dont le prix est compris 
 *          (>= price 12000)		entre 12000 et 19500 euros.
 *          (<= price 19500) )
 *
 * La structure en arbre peut se représenter de la manière suivante :
 *				&&
 *				.
 *		.....................................................
 *		.               .                 .                 .
 *	       ==              ||                 >=               <=
 *	      .  .              .                .   .            .   .
 *	 model   1M4   ...................   price  12000    price   19500
 *                     .                 .
 *                    ==                ==
 *                   .  .              .  .
 *        grBodyStyle   S2    grBodyStyle  S3
 *
 * @param	Array(n)	tuple à valider
 * @param	Array(n)	critères de validation
 *
 * @return	bool		indique si les conditions sont respectées par 
 *				le tuple
 */
function respects_conditions(tuple, conditions)
{
	var operation = conditions[0];
	var result;
	if ( (operation == "&&") || (operation == "||") )
	{
		// operateur booléen, pas de comparaison
		var temp_results = new Array();
		var i = 1;
		
		result = (operation == "&&");
		//SFGACFR
		//while ( result && (i < conditions.length) )
		while (i < conditions.length)
		//SFGACFR
		{
			if ( operation == "&&" )
			{
				result = result && respects_conditions(tuple, conditions[i]);
			}
			else
			{
				result = result || respects_conditions(tuple, conditions[i]);
			}
			i++;
		}

		return result;
	}
	else
	{
		// operateur de comparaison (terminaison de la récursivité)
		var expr1 = conditions[1];
		var expr2 = conditions[2];
		if ( tuple[expr1] != "" )
		{
			// le 1er parametre est un nom de colonne du tuple
			var expr1 = tuple[expr1];
		}

		switch ( operation )
		{
			case "==":
				result = (expr1 == expr2);
				break;
			case ">":
				result = (parseFloat(expr1) > parseFloat(expr2));
				break;
			case ">=":
				result = (parseFloat(expr1) >= parseFloat(expr2));
				break;
			case "<=":
				result = (parseFloat(expr1) <= parseFloat(expr2));
				break;
			case "<":
				result = (parseFloat(expr1) < parseFloat(expr2));
				break;
			case "!=":
				result = (expr1 != expr2);
				break;
		}
		return result || (expr1 === "") || (expr2 === "");
	}
}

/* Fonction de sélection plus poussée. Permet d'effectuer sélections, 
 * projections, tris, etc.
 *
 * @param	Array(n)	tableau associatif indiquant les éventuelles 
 *				projection à effetuer à sélectionner. Si ce 
 *				parametre n'est pas un tableau, ou si sa taille
 *				est nulle, sélectionne toutes les propriétés 
 *				des n-uplets staisfaisants.
 * @param	Array(n)	tableau des critères de sélection. Représente 
 *				une structure d'arbre
 * @param	Array(n)	tableau indiquant les différents critères de 
 *				tri du résultat
 * @param	int		nombre de résultats à retourner
 * @param	boolean		indique si les résultats doivent être 
 *				identitaires, ou si les doublons sont à 
 *				conserver
 *
 * @return	Array(n)	tableau contenant les tuples satisfaisant aux 
 *				critères de sélection
 */
function select(what, where, order_by, limit, identity)
{
	var tab_result = new Array();

	// chaque tuple de la table de données est examiné successivement
	for ( var key in tab_versions )
	{
		var tuple = tab_versions[key];

		if ( (where.length == 0)
			|| respects_conditions(tuple, where) )
		{
			// tous les criteres de selection sont acceptes
			// ajout à la liste résultat
			tab_result.push(tuple);
		}		
	}

	// tri du résultat
	for ( var key in order_by )
	{
		var sort_criterium = order_by[key];
		// go on for a quicksort		
		quicksort(tab_result, 0, tab_result.length - 1, sort_criterium[0]);
		if ( sort_criterium[1] == "desc" )
		{
			// reverses order...
			tab_result.reverse();
		}
	}

	if ( what != "" )
	{
		projection(tab_result, what);
	}

	// build result
	var final_result = new Array();
	var i = 0;

	if ( identity || (limit != "") )
	{
		if ( limit == "" )
		{
			limit = tab_result.length;
		}

		// recopie des premiers résultats
		while ( (i < tab_result.length) && (final_result.length < limit) )
		{
			if ( !identity || !in_array(final_result, tab_result[i]) )
			{
				final_result.push(tab_result[i]);
			}
			i++;
		}
	}
	else
	{
		final_result = tab_result;
	}

	return final_result;
}

//SFGACFR
//version generique de la fonction select ci-dessus (permet de specifier la table de recherche)
function select_from(what, where, from, order_by, limit, identity)
{
	var tab_result = new Array();

	// chaque tuple de la table de données est examiné successivement
	for ( var key in from )
	{
		var tuple = from[key];

		if ( (where.length == 0)
			|| respects_conditions(tuple, where) )
		{
			// tous les criteres de selection sont acceptes
			// ajout à la liste résultat
			tab_result.push(tuple);
		}		
	}

	// tri du résultat
	for ( var key in order_by )
	{
		var sort_criterium = order_by[key];
		// go on for a quicksort		
		quicksort(tab_result, 0, tab_result.length - 1, sort_criterium[0]);
		if ( sort_criterium[1] == "desc" )
		{
			// reverses order...
			tab_result.reverse();
		}
	}

	if ( what != "" )
	{
		projection(tab_result, what);
	}

	// build result
	var final_result = new Array();
	var i = 0;

	if ( identity || (limit != "") )
	{
		if ( limit == "" )
		{
			limit = tab_result.length;
		}

		// recopie des premiers résultats
		while ( (i < tab_result.length) && (final_result.length < limit) )
		{
			if ( !identity || !in_array(final_result, tab_result[i]) )
			{
				final_result.push(tab_result[i]);
			}
			i++;
		}
	}
	else
	{
		final_result = tab_result;
	}

	return final_result;
}
//SFGACFR