var GAUCHE_VERS_DROITE = 1;
var DROITE_VERS_GAUCHE = -1;

/**
 * Affiche un message de debug
 * 
 * @param message
 * @return

function debug(message) {
	var my_div = document.createElement('div');
	my_div.innerHTML = message + '<br/>';

	$('debug').appendChild(my_div);
}
 */

// code JS pour tester la mosaique dans une page statique
/*
Event.observe(window, 'load', function() {
	debug('debut');
	// création d'une premiére mosaique
		moz = new Mosaique('conteneurDeBloc', GAUCHE_VERS_DROITE, true);
		moz.ajouterListeElements('#reserveBloc1 .bloc');
		debug('premiere mosaique créée');
		numBloc = 0;

		// création de la deuxième mosaique
		moz2 = new Mosaique('conteneurDeBloc2', DROITE_VERS_GAUCHE, false);

		moz2.ajouterListeElements('#reserveBloc2 .bloc');
		nouveaubloc = $$('#reserveBloc3 #produit-16');
		if (nouveaubloc) {
			// moz2.ajouterElement(nouveaubloc[0]);
	} else {
		debug('pas de nouveau bloc');
	}

	// création de la deuxième mosaique
	moz3 = new Mosaique('conteneurDeBloc4', DROITE_VERS_GAUCHE, false);
	b1 = $$('#reserveBloc4 #produit-01');
	b2 = $$('#reserveBloc4 #produit-02');
	b3 = $$('#reserveBloc4 #produit-03');
	moz3.ajouterElement(b1[0]);
	moz3.ajouterElement(b2[0]);
	moz3.ajouterElement(b3[0]);

	moz4 = new Mosaique('conteneurDeBloc5', DROITE_VERS_GAUCHE, false);
	b4 = $$('#reserveBloc4 #produit-04');
	moz4.ajouterElement(b4[0]);
	b5 = $$('#reserveBloc4 #produit-05');
	moz4.ajouterElement(b5[0]);
	b6 = $$('#reserveBloc4 #produit-06');
	moz4.ajouterElement(b6[0]);
	b7 = $$('#reserveBloc4 #produit-07');
	moz4.ajouterElement(b7[0]);
	b8 = $$('#reserveBloc4 #produit-08');
	moz4.ajouterElement(b8[0]);
	b9 = $$('#reserveBloc4 #produit-09');
	moz4.ajouterElement(b9[0]);
	b10 = $$('#reserveBloc4 #produit-10');
	moz4.ajouterElement(b10[0]);

});
*/
/**
 * Class Bloc Contient les informations d'un bloc affiché dans la mosaique
 * 
 * @param domElement
 *            l'élement dom correspondant au bloc
 * @param mosaique
 *            la mosaique dans laquelle le bloc est positionné
 * @return
 */
var Bloc = Class.create();
Bloc.prototype = {

	initialize : function(domElement, mosaique) {
		
		this.domElement = domElement;
		this.largeur = domElement.getWidth();
		this.hauteur = domElement.getHeight();
		this.mosaique = mosaique;
	},

	/**
	 * Place le bloc dans la mosaique en modifiant le css de l'élément dom
	 * associé
	 * 
	 * @param x
	 *            l'abscisse du bloc
	 * @param y
	 *            l'ordonnée du bloc
	 * @return
	 */
	positionner : function(x, y, afficherElement) {
		this.x = x;
		this.y = y;

		// le bloc est positionné dans le conteneur
		// this.mosaique.conteneur.append(bloc.domElement);
		this.mosaique.conteneur.appendChild(this.domElement);
	
		// modification du css pour les coord en absolute
		if (this.mosaique.sensDeRemplissage == GAUCHE_VERS_DROITE) {
			// this.domElement.css( {"left": x+'px', 'top':y+'px'} );
			this.domElement.setStyle( {
				"left" : x + 'px',
				'top' : y + 'px'
			});
	
		} else {
			this.domElement.setStyle( {
				"right" : x + 'px',
				'top' : y + 'px'
			});
		};
		
		if(afficherElement){
			this.domElement.setStyle( {
				"display" : 'block'
			});	
		}else{
			this.domElement.setStyle( {
				"display" : 'none'
			});
		}

	},

	/**
	 * Retourne l'altitude du bloc (ordonnée + hauteur)
	 * 
	 * @return l'altitude du bloc
	 */
	getAltitude : function() {
		return parseInt(this.y) + parseInt(this.hauteur);
	},
	
	/**
	 * Retourne l'abscisse du bord le plus proche de l'orgine (bord droit ou gauche
	 * selon le sens de remplissage)
	 * 
	 * @return l'abscisse du bord le plus proche de l'orgine
	 */
	getBordAmont : function() {
		return this.x;
	},
	
	/**
	 * Retourne l'abscisse du bord le plus éloigné de l'orgine (bord droit ou gauche
	 * selon le sens de remplissage)
	 * 
	 * @return l'abscisse du bord le plus éloigné de l'orgine
	 */
	getBordAval : function() {
		return parseInt(this.x) + parseInt(this.largeur);
	}
};
/**
 * Class Dispo Contient les informations d'un emplacement de diponibilité dans
 * la mosaique
 * 
 * @param x
 *            l'abscisse de la disponibilité
 * @param y
 *            l'ordonnée de la disponibilite
 * @return
 */
var Dispo = Class.create();
Dispo.prototype = {

	initialize : function(x, y, largeur, hauteur) {
		this.x = x;
		this.y = y;
		this.largeur = largeur;
		this.hauteur = hauteur;
	},

	/**
	 * Retourne un représentation textuelle de la disponibilité (debug)
	 * 
	 * @return
	 */
	toString : function() {
		return 'Dispo[' + this.x + ', ' + this.y + ', ' + this.largeur + ', '
				+ this.hauteur + ']';
	}
};

/**
 * Class Mosaique Contient les informations d'une mosaique de bloc
 * 
 * @param conteneurPath
 *            le chemin de l'élement conteneur dans le dom (div à l'interieur de
 *            laquelle les blocs se positionne)
 * @param elementsPath
 *            le chemin des éléments à considérer comme des blocs
 * @param sensDeRemplissage
 *            le sens de remplissage (1 ou -1)
 * @param boucheTrou
 *            true si les trous doivent être comblés, sinon false (un bloc ne
 *            peut pas alors être placé + bas que le précédent)
 * @param afficherElement
 *            affiche ou non les éléments après les avoir placés dans le mosaique
 * @return
 */
var Mosaique = Class.create();
Mosaique.prototype = {

	initialize : function(conteneurPath, sensDeRemplissage, boucheTrou, afficherElement) {
		this.sensDeRemplissage = sensDeRemplissage;
		this.conteneur = $(conteneurPath);
		this.dispos = new Array();
		this.blocs = new Array();
		this.boucheTrou = boucheTrou;
		this.dernierBlocPlace = null;
		this.nombreDeBloc = 0;
		this.afficherElement=afficherElement;

		// récupère l'information de hauteur dans le css
		hauteurCss = this.conteneur.getStyle('height');
		hauteurCss = hauteurCss.sub('px', '');
		// si la hauteur est défini, elle est figé, aucun bloc sera placé au
		// dela.
		// si la hauteur n'est pas défini, la hauteur de la mosaique s'adapte
		// aux blocs insérés.
		if (hauteurCss > 159) { // Hauteur minimum pour placer un élément dans la mosaique.
			this.hauteurMax = hauteurCss;
		} else {// Si une hauteur a été défini dans le css, cette hauteur est la hauteur max. Sinon, il n'y a pas de hauteur max (hauteur en +infini = 100000)
			this.hauteurMax = 100000;
		}

		// création de la première dispo dans le coin 0,0
		dispo = new Dispo(0, 0, this.conteneur.getWidth(), this.getHauteurMax());
		this.dispos.push(dispo);
	},
	
	/**
	 * Retourne la hauteur max de la mosaique.
	 * @return
	 */
	getHauteurMax : function() {
			return this.hauteurMax;
	},

	/**
	 * Change la hauteur de la mosaique
	 * 
	 * @param hauteur
	 *            nouvelle hauteur de la mosaique
	 */
	setHauteur : function(hauteur) {
		this.conteneur.setStyle( {
			height : hauteur + 'px'
		});
	},

	/**
	 * Ajoute une disponibilité à la liste des disponibilités existantes en
	 * effectuant quelques controles
	 * 
	 * @param nouvelleDispo
	 *            la nouvelle disponibilié
	 * @param force
	 *            si true les règles de contrôles sont ignorées
	 * @return
	 */
	ajouterDispo : function(nouvelleDispo, forceMiseAJour) {
		if (nouvelleDispo.largeur == 0) {
			return false;
		}
		for ( var i = 0; i < this.dispos.length; i++) {
			dispo = this.dispos[i];

			/*
			 * //si la nouvelle dispo a le même x, et est comprise dans le
			 * hauteur d'une dispo existante if(dispo.x == nouvelleDispo.x &&
			 * dispo.y<=nouvelleDispo.y &&
			 * (dispo.y+dispo.hauteur)>=nouvelleDispo.y ){ //debug('la nouvelle
			 * dispo '+nouvelleDispo.toString()+' est comprise dans la hauteur d
			 * une dispo existante '+dispo.toString()); //return; }
			 */

			// si la dispo a le même y, et est comprise dans la largeur d'une
			// dispo existante
			if (dispo.y == nouvelleDispo.y
					&& (dispo.x <= nouvelleDispo.x && (dispo.x + dispo.largeur) >= nouvelleDispo.x)) {
				if (forceMiseAJour) {
					this.dispos.push(nouvelleDispo);
//					  dispo.largeur=dispo.largeur-(nouvelleDispo.x-dispo.x);
//					  dispo.x=nouvelleDispo.x; 
					 
				} else {
					/*
					console.log('la nouvelle dispo '
							+ nouvelleDispo.toString()
							+ ' est comprise dans la largeur d une dispo existante '
							+ dispo.toString());
					*/
				}
				
				return;
			}

			// cas où les 2 dispos ont exactement les mêmes coordonnées
			if (dispo.x == nouvelleDispo.x && dispo.y == nouvelleDispo.y) {
				//console.log(nouvelleDispo.toString() + ' non ajouté car coord identique');
				return false;
			}

		}
		this.dispos.push(nouvelleDispo);
	},

	/**
	 * Met à jour les disponibilités par rapport à l'ajout d'un bloc
	 * 
	 * @param bloc
	 *            le bloc nouvellement ajouté
	 * @return
	 */
	mettreAJourDispoInferieur : function(bloc) {

		nouvellesDispos = new Array();

		// parcours les disponibilités
		for ( var i = 0; i < this.dispos.length; i++) {

			dispo = this.dispos[i];

			// cas des disponibilité dans la hauteur du bloc (y de la dispo
			// entre le bas et le haut du bloc)
			if (dispo.y >= bloc.y && dispo.y < bloc.getAltitude()) {
				// console.log('avant: '+dispo.toString());

				// mise a jour de la largeur de la dispo
				if (dispo.x <= bloc.getBordAmont()
						&& (dispo.x + dispo.largeur) >= bloc.getBordAmont()) {
					dispo.largeur = bloc.getBordAmont() - dispo.x;
				}
				
				// si le bloc vient se placer au milieu de la dispo (création
				// d'un nouvelle dispo pour la partie aval de la dispo)
				if (dispo.x <= bloc.getBordAmont()
						&& (dispo.x + dispo.largeur) >= bloc.getBordAval()) {
					nouvelleDispo = new Dispo(bloc.getBordAval(), dispo.y, this.conteneur.getWidth() - bloc.getBordAval(), this.getHauteurMax() - dispo.y);
					nouvellesDispos.push(nouvelleDispo);
				}
			}

			// cas des disponibilité en dessous du bloc dont la hauteur
			// dépasserait le bas du bloc (cas ou le bloc vient 'fermer' une
			// dispo, créant un trou)
			if (dispo.y < bloc.y && (dispo.y + dispo.hauteur) > bloc.y) {

				if (dispo.x >= bloc.x && dispo.x < bloc.getBordAval()) {
					dispo.hauteur = bloc.y - dispo.y;
				}
			}

		}

		// ajoute les dispos créée dans cette fonction dans la liste de dispo
		for ( var int2 = 0; int2 < nouvellesDispos.length; int2++) {
			this.ajouterDispo(nouvellesDispos[int2]);
		}
	},

	/**
	 * Recherche un bloc en aval d'un point (hauteur du bloc autour de y, le +
	 * proche possible de x)
	 * 
	 * @param x
	 *            abscisse du point
	 * @param y
	 *            ordonnée du point
	 * @return le bloc trouvé (éventuellement null)
	 */
	trouverBlocAval : function(x, y) {
		var blocResultat = null;
		for ( var i = 0; i < this.blocs.length; i++) {
			bloc = this.blocs[i];
			// console.log('considere bloc '+bloc.x+', '+bloc.y+', '+bloc.hauteur);
			if (bloc.x >= x && bloc.y <= y && bloc.getAltitude() > y) {
				if (blocResultat == null || (bloc.x < blocResultat.x)) {
					blocResultat = bloc;
				}
			}
		}
		return blocResultat;
	},

	/**
	 * Recherche un bloc en amont d'un point (hauteur du bloc autour de y, le +
	 * proche possible de x)
	 * 
	 * @param x
	 *            abscisse du point
	 * @param y
	 *            ordonnée du point
	 * @return le bloc trouvé (éventuellement null)
	 */
	trouverBlocAmont : function(x, y) {
		//console.log('recherche du bloc a gauche de ' + x + ', ' + y);
		var blocResultat = null;
		for ( var i = 0; i < this.blocs.length; i++) {
			bloc = this.blocs[i];
			//console.log('considere bloc '+bloc.x+', '+bloc.y+', '+bloc.hauteur);
			if (bloc.x <= x && bloc.y <= y && bloc.getAltitude() > y) {
				if (blocResultat == null || (bloc.x > blocResultat.x)) {
					blocResultat = bloc;
					//console.log('bingo');
				}
			}
		}
		return blocResultat;
	},
	
	trouverBlocEnDessous : function(x, y) {
		//console.log('recherche du bloc en dessous de ' + x + ', ' + y+' dans un tableau de '+this.blocs.length);
		var blocResultat = null;
		for ( var i = 0; i < this.blocs.length; i++) {
			bloc = this.blocs[i];
			//console.log('considere bloc '+bloc.x+', '+bloc.y+', '+bloc.largeur+', '+bloc.hauteur);
			if (bloc.getBordAmont() <= x && bloc.getBordAval() > x && bloc.y >= y) {
				if (blocResultat == null || (bloc.y < blocResultat.y)) {
					blocResultat = bloc;
					//console.log('bingo');
				}
			}
		}
		return blocResultat;
	},

	/**
	 * Créé de nouvelle disponibilité suite à l'ajout d'un nouveau bloc. On
	 * cherche à créer des disponibilités dans les coin aval/bas et amont/haut
	 * 
	 * @param bloc
	 *            le bloc nouvellement ajouté
	 * @return
	 */
	creerNouvelleDispo : function(bloc) {

		//console.log('creation d une dispo suite à l ajout du bloc '+bloc.toString());
		// --- coin Aval/Bas

		// recherche d'un bloc en aval du coin aval/bas du bloc (si remplissage
		// gauche/droite, le coin droite/bas)
		blocAval = this.trouverBlocAval(bloc.getBordAval(), bloc.y);

		// si un bloc est trouvé, son bord amont conditionne la largeur max de
		// la dispo, sinon c'est la limite du conteneur
		if (blocAval) {
			xMax = blocAval.getBordAmont();
		} else {
			xMax = this.conteneur.getWidth();
		}

		// recherche d'un bloc en dessous du bloc
		blocEnDessous = this.trouverBlocEnDessous(bloc.x, bloc.getAltitude());
		if (blocEnDessous) {
			yMax = blocEnDessous.y;
		} else {
			yMax = this.getHauteurMax();
		}
		
		// ajout de la dispo
		//console.log('nouvelleDispoCoinBas');
		nouvelleDispoCoinBas = new Dispo(bloc.getBordAval(), bloc.y, xMax - bloc.getBordAval(), yMax - bloc.y);
		//console.log('ajout d une dispo dans le coin bas');
		this.ajouterDispo(nouvelleDispoCoinBas, true);

		// --- coin Amont/Haut
		// recherche d'un bloc en amont du coin amont/haut du bloc (si
		// remplissage gauche/droite, le coin gauche/haut)
		//console.log('-----------recherche du bloc amont-------------');
		blocAmont = this.trouverBlocAmont(bloc.getBordAmont(), bloc.getAltitude());
		// si un bloc est trouvé, son bord aval conditionne le point de départ
		// de la dispo, sinon c'est l'origine du conteneur
		if (blocAmont) {
			//console.log('bloc amont ' + blocAmont.x + ', ' + blocAmont.y);
			xMin = blocAmont.getBordAval();
		} else {
			//console.log('pas de bloc amont');
			xMin = 0;
		}

		// recherche d'un bloc en aval du coin aval/haut du bloc (si remplissage
		// gauche/droite, le coin aval/haut)
		blocAvalHaut = this.trouverBlocAval(bloc.getBordAval(), bloc
				.getAltitude());
		// si un bloc est trouvé, son bord amont conditionne la largeur de la
		// dispo, sinon c'est la limite du conteneur
		if (blocAvalHaut) {
			//console.log('bloc a blocAvalHaut ' + blocAvalHaut.x + ', '+ blocAvalHaut.y);
			xMax = blocAvalHaut.getBordAmont();
		} else {
			//console.log('pas de bloc blocAvalHaut ');
			xMax = this.conteneur.getWidth();
		}
		// ajout de la dispo
		//console.log('nouvelleDispoCoinHaut');
		nouvelleDispoCoinHaut = new Dispo(xMin, bloc.getAltitude(), xMax - xMin, yMax - bloc.getAltitude());
		this.ajouterDispo(nouvelleDispoCoinHaut, true);

	},

	/**
	 * Recherche une disponibilité pour un bloc. Les dispos sont parcourus afin
	 * de trouver la dispo ayant la largeur et la hauteur nécessaire, la plus
	 * basse et la plus proche de l'origine possible.
	 * 
	 * @param bloc
	 *            le bloc à placer
	 * @return la meilleur dispo pour le bloc
	 */
	rechercherDispo : function(bloc) {
		//console.log('Recherche d une dispo');

		var dispoCandidate;
		for ( var i = 0; i < this.dispos.length; i++) {

			dispo = this.dispos[i];

			if (dispo.largeur > 0) {
				//console.log('---' + dispo.toString());
			}

			if (dispoCandidate == null
					&& dispo.largeur >= bloc.largeur
					&& dispo.hauteur >= bloc.hauteur
					&& (this.boucheTrou || this.dernierBlocPlace == null || dispo.y >= this.dernierBlocPlace.y)) {
				dispoCandidate = dispo;
			}

			// dans le cas où l'on bouche les trou, la dispo la plus basse est
			// privilégié. Sinon seul les dispos au moins aussi haute que le
			// dernier bloc sont envisagées.
			if (this.boucheTrou || this.dernierBlocPlace == null
					|| dispo.y >= this.dernierBlocPlace.y) {

				if (dispo.largeur >= bloc.largeur
						&& dispo.hauteur >= bloc.hauteur) {
					if (dispo.y < dispoCandidate.y) {
						dispoCandidate = dispo;
						// console.log('-> dispo + basse');
					} else if (dispo.y == dispoCandidate.y
							&& dispo.x < dispoCandidate.x) {
						dispoCandidate = dispo;
						// console.log('-> dispo + proche du bord');
					} else {
						// console.log('-> pas mieux');
					}

				} else {
					/*
					 * 
					 * if(dispo.largeur < bloc.largeur){ debug('largeur de la
					 * dispo trop petite'); } else if(dispo.hauteur <
					 * bloc.hauteur){ debug('pas la hauteur nécessaire'); } else
					 * if(dispoCandidate!=null && dispo.y > dispoCandidate.y){
					 * debug('il existe dispo + basse'); } else
					 * if(dispoCandidate!=null && dispo.x > dispoCandidate.x){
					 * debug('il existe dispo + à gauche'); } else{ debug('autre
					 * raison'); }
					 */
				}
			} else {
				console.log('boucheTrou: ' + this.boucheTrou);
				console.log('this.dernierBlocPlace: ' + this.dernierBlocPlace.y);
			}
		}

		return dispoCandidate;
	},

	/**
	 * Positionne le bloc dans la mosaique
	 * 
	 * @param bloc
	 *            le bloc à positionner
	 * @return
	 */
	positionnerBloc : function(bloc) {

		// une dispo est recherché
	dispo = this.rechercherDispo(bloc);
	
	// si une dispo est trouvé (aucune dispo ne peut etre trouvé si plus de
	// place dans le conteneur ou en cas d'erreur)
	if (dispo != null) {
		//console.log('dispo choisie ' + dispo.toString());

		// positionne le bloc sur cette dispo
		bloc.positionner(dispo.x, dispo.y, this.afficherElement);

		// la dispo est rendu inutilisable en mettant sa largeur et hauteur à 0.
		dispo.largeur = 0;
		dispo.hauteur = 0;

		// conséquence de l'ajout d'un bloc: mise à jour et création de nouvelle
		// dispo
		this.creerNouvelleDispo(bloc);
		this.mettreAJourDispoInferieur(bloc);

		// mémorise le dernier bloc posé (pour le cas où l'on ne souhaite pas
		// boucher les trous, alors le futur bloc ne devra pas être positionné
		// plus bas)
		this.dernierBlocPlace = bloc;
		this.blocs.push(bloc);

	} else {
		console.log('pas de place trouvée pour le bloc');
	}
},

/**
 * Ajoute un élement dans la mosaique
 * 
 * @param domElement
 *            l'élément du DOM à rajouter dans la mosaique
 * @return
 */
ajouterElement : function(domElement, avecAnimation) {
	//console.log('ajout de '+domElement);
	if(domElement){
		this.nombreDeBloc++;
		var bloc = new Bloc(domElement, this);
		
		this.positionnerBloc(bloc);
		
		// hauteur de la mosaique
		if (bloc.getAltitude() > this.conteneur.getHeight()) {
			this.setHauteur(bloc.getAltitude());
		}
		
		
		if(avecAnimation){
			new Effect.multiple(new Array(domElement), Effect.Appear, null);
		}
		
		
	}else{
		//console.log('ajouterElement:  l element n\'est pas défini');
	}

},



/**
 * Ajoute une liste d'élement dans la mosaique à partir d'un chemin
 * 
 * @param elementsPath
 *            chemin des l'éléments du dom considérés comme des blocs
 * @return
 */
/*
ajouterListeElements : function(elementsPath) {
	this.ajouterListeElements(elementsPath, false);
},
*/
ajouterListeElements : function(elementsPath, avecAnimation) {
	// pour chaque bloc, création d'un objet Bloc et recherche d'une position
	//alert('début de la construction');
	mosaique = this;
	if (elementsPath != null) {
		$$(elementsPath).each(function(b) {
			mosaique.ajouterElement(b, avecAnimation);
			
		});
	}
	//alert('fin de la construction');
},




supprimerElement : function(element, supprimerBaliseHTML) {

	//console.log(element);
	largeur = element.getWidth();
	hauteur = element.getHeight();
	if(this.sensDeRemplissage == GAUCHE_VERS_DROITE){
		posX = element.getStyle('left').gsub('px','');
	}else{
		posX = element.getStyle('right').gsub('px','');
	}
	posY = element.getStyle('top').gsub('px','');
	//console.log(element.getStyle('right')+',  '+element.getStyle('left'));
	//console.log('largeur: '+largeur+',  hauteur: '+hauteur+'  x: '+posX+',  y: '+posY);
	
	// ajout de la dispo
	nouvelleDispo = new Dispo(posX, posY, largeur,hauteur);
	this.ajouterDispo(nouvelleDispo, true);
	
	//console.log('suppression du bloc dans la mémoire de la mosaique');
	for ( var i = 0; i < this.blocs.length; i++) {
		bloc = this.blocs[i];
		if(bloc.x==posX && bloc.y==posY){
			//console.log('suppression du bloc dans la mémoire de la mosaique');
			this.blocs.splice(i,1);
		}
	}
	
	//possibilité de ne pas supprimer la balise HTML tout de suite (pour laisser les bordures affichées par exemple)
	if(supprimerBaliseHTML){
		divTypeC.remove();
	}
	
	//new Effect.multiple(new Array(divTypeC), Effect.Fade, {afterFinish: function(){divTypeC.remove();} });	
}

}


