/*


.-.         .-.               _        .-. .-.            .-.   
: :         : :              :_;      .' `.: :            : :   
: :   .--.  : `-. .-..-..--. .-.,-.,-.`. .': `-.  .--.    : `-. 
: :_ ' .; ; ' .; :: :; :: ..': :: ,. : : : : .. :' '_.' _ : .. :
`.__;`.__,_;`.__.'`._. ;:_;  :_;:_;:_; :_; :_;:_;`.__.':_;:_;:_;
                   .-. :                                        
                   `._.'                                        


FICHIER	: labyrinthe.h
DATE	: Fevrier 2003
AUTEUR	: Aymeric
EMAIL	: Aymeric.Demartin@iup.univ-avignon.fr
GROUPE	: IUP1 GMI Avignon - Groupe 5 (RT)

*/



#ifndef LABYRINTHE_H
#define LABYRINTHE_H

#include "global.h"
#include "file.h"

class Labyrinthe {
	private :
		char **matrice;
		int taille;
		int densite;
		position cible;
	public :
		Labyrinthe(int, int);
		~Labyrinthe();
                void setTaille(int);
                void setDensite(int);
                void SetPositionCible(int, int);
		char RetourneContenu(int, int);
		int RetourneTaille() const;
		int RetourneDensiteLabyrinthe() const;
		position RetournePositionCible() const;
		bool CaseVide(int, int);
		bool CaseMur(int, int);
                void newMatrice();
                void faitLesMurs();
                void remplideVide();
                void affiche_labyrinthe(int) const;
                void GenereBlocs(int);
                void place_cible();
		int verifie_prochaine_case(int, int);
                void bouge_joueur(int, int, int, int, int);
                void PlaceJoueur(int, int, int);
		bool PlusCourtChemin(int, int, int, int, bool, int);
		int position2int (int, int);
		position int2position (int);
		int ToutePlusProcheLibre(int, int[]);
		position PlusProcheLibre(int, int);

};





/*-------------
  CONSTRUCTEURS
  -------------*/		
Labyrinthe::Labyrinthe(int dim, int d)
{
	int i,j;

	// on definit la taille de notre labyrinthe
	taille=dim;

	// on definit la densite de notre labyrinthe
	densite=d;

	// on cree la matrice
	newMatrice();

	// on fait les murs
	faitLesMurs();

	// on appelle cette fonction pour generer aleatoire les blocs
	GenereBlocs(0);
}

/*-----------
  DESTRUCTEUR
  -----------*/
Labyrinthe::~Labyrinthe()
{
	for (int i=0;i<taille;i++)
			delete matrice[i];
	delete matrice;
}

/*----------
  ACCESSEURS
  ----------*/
// on definit la taille
void Labyrinthe::setTaille(int dim)
{
	taille=dim;
}

// on definit la densite
void Labyrinthe::setDensite(int d)
{
	densite=d;
}

// on definit la position de la cible
void Labyrinthe::SetPositionCible(int px, int py)
{
	cible.x=px;
	cible.y=py;
}

// on retourne la taille du labyrinthe
int Labyrinthe::RetourneTaille() const
{
	return taille;
}
// on retourne le contenu d'une case
char Labyrinthe::RetourneContenu(int a, int b)
{
	return matrice[a][b];
}

// on retourne la densite
int Labyrinthe::RetourneDensiteLabyrinthe() const
{
	return densite;
}

// on retourne la position de la cible
position Labyrinthe::RetournePositionCible() const
{
	return cible;
}


/*---------
  FONCTIONS
  ---------*/
  
// cree la matrice
void Labyrinthe::newMatrice()
{
	int i;
	matrice=new char *[taille];
	for (i=0; i<taille; i++)
	{
			matrice[i]=new char[taille];
	}
}


// place les murs de la matrice
void Labyrinthe::faitLesMurs()
{
	int i,j;
	// remplissage des murs de la matrice par le symbole '#'
	// remplissage par des espaces vides toutes les autres cases
	for (i=0; i < taille; i++) matrice[0][i]='#';
	for (i=1; i < taille; i++)
	{
			matrice[i][0]='#';
			for (j=1; j < taille-1; j++) matrice[i][j]=' ';
			matrice[i][taille-1]='#';
	}
	for (i=0; i < taille; i++) matrice[taille-1][i]='#';
}

// rempli d'espace la matrice
void Labyrinthe::remplideVide()
{
	int i,j;
	for (i=0; i<taille; i++)
		for (j=0; j<taille; j++) matrice[i][j]=' ';
}

// fonction permettant l'affichage du labyrinthe
void Labyrinthe::affiche_labyrinthe(int mode) const
{
	int i,j;
	for (i=0; i < taille; i++)
	{
		for (j=0; j < taille; j++)
		{
			if (matrice[i][j] == ' ') printw(".");
			else printw("%c",matrice[i][j]);
		}
		printw("\n");
	}
}

// cette fonction retourne true si la case a la position specifiee en parametre est vide
bool Labyrinthe::CaseVide(int x, int y)
{
	if (matrice[x][y] == ' ') return true;
	else return false;
}

// cette fonction retourne true si la case a la position specifiee en parametre contient le caractere '#'
bool Labyrinthe::CaseMur(int x, int y)
{
	if (matrice[x][y] == '#') return true;
	else return false;
}

// cette methode permet de placer la cible au centre du labyrinthe
void Labyrinthe::place_cible()
{
	matrice[(taille-2)/2+1][(taille-2)/2+1]='@';
	SetPositionCible((taille-2)/2+1,(taille-2)/2+1);
}

// cette methode va generer les blocs aleatoirement selon la densite
void Labyrinthe::GenereBlocs(int diminution)
{
	// la densite donne le pourcentage de cases vides, dans mon cas je vais plutot utiliser le pourcentage de cases remplies
	int d=100-densite;
	// si on a un labyrinthe de taille 10, on aura 10*10 cases, donc si on a une densite de 50%, on veut avoir 50% de 10*10,
	// c'est-a-dire 50 cases vides (sans prendre en compte les murs)
	d=( (d*taille*taille) - ((taille-1) * 4) ) / 100;
	int max=taille-1;
	int j=1,x,y;

	srandom(getpid()+diminution);

	while( j <= d )
	{
		x = ( random() % max ) + 1;
		y = ( random() % max ) + 1;
		if( CaseVide(x,y) == true )
		{
			matrice[x][y] = '#';
			j++;
		}
	}
}

// cette methode permet de bouger le numero du joueur en cours
void Labyrinthe::bouge_joueur(int id, int old_x, int old_y, int x, int y)
{
	matrice[old_x][old_y]=' ';
	matrice[x][y]=num[id-1];
}

// cette methode permet de verifier si on peut bouger le joueur dans la case demandee
int Labyrinthe::verifie_prochaine_case(int x, int y)
{
	if (CaseMur(x,y) == true) return 0;
	else if ((cible.x == x) && (cible.y == y)) return 2;
	else return 1;
}

// cette fonction retourne la position de la case la plus proche qui est libre
// elle est utilisee dans le placement des joueurs au debut du labyrinthe
position Labyrinthe::PlusProcheLibre(int ex, int ey)
{
	int i=ex-1;
	int j;
	int k,l=0,m,n=1;
	position libre;
	position t[taille*taille];
	t[l].x=-1;
	t[l].y=-1;

	while (t[0].x == -1)
	{
		for (k=0; k < 3+2*(n-1); k++)
		{
			j=ey-n;
			for (m=0; m < 3+2*(n-1); m++)
			{
				if ((i >= 0) && (j >= 0) && (i < taille) && (j < taille))
				{
					if (CaseVide(i,j))
					{
						t[l].x=i;
						t[l].y=j;
						l++;
					}
					j++;
				}
			}
			i++;
		}
		n++;
		if (n == taille) break;
		i=ex-n;
	}
	if (t[0].x == -1)
	{
		libre.x=-1;
		libre.y=-1;
	}
	else
	{
		libre.x=2;
		libre.y=2;
		srandom(getpid());
		i=random() % l;
		libre.x=t[i].x;
		libre.y=t[i].y;
	}
	return (libre);
}

void Labyrinthe::PlaceJoueur(int ex, int ey, int i)
{
	char c=num[i-1];
	matrice[ex][ey]=c;
}

// cette fonction convertit des coordonnees en un int
int Labyrinthe::position2int (int ex, int ey)
{
	return (ex*taille + ey);
}

// cette fonction convertit un int en coordonnee pour notre matrice
position Labyrinthe::int2position (int e)
{
	position a;
	a.x=e/taille;
	a.y=e%taille;
	return (a);
}

// cette fonction regarde toutes les cases libres ou le joueur peut se deplacer dans le calcul du plus court chemin
int Labyrinthe::ToutePlusProcheLibre(int e,int t[])
{
	int nblibre=0;
	int m;
	int ex=int2position(e).x;
	int ey=int2position(e).y;
	int i=ex - 1;
	int j=ey;

	for (m=0; m<4; m++)
	{
		if ( (verifie_prochaine_case(i,j) == 2) || (verifie_prochaine_case(i,j) == 1) )
		{
			t[nblibre]=position2int(i,j);
			nblibre++;
		}
		if (j == ey-1) j=ey+1;
		if (i == ex+1)
		{
			i=ex;
			j=ey-1;
		}
		if (i == ex-1) i=ex+1;
	}
	return (nblibre);
}

// cette fonction permet de calculer le plus court chemin d'un point a un autre
bool Labyrinthe::PlusCourtChemin(int ax, int ay, int bx, int by, bool affiche, int id)
{
	int C[taille*taille];
	int k,l=0,nb,nblibre=0,i;
	bool marquage[taille*taille];
	int t[4];

	for (i=0; i<taille*taille; i++)
	{
		C[i]=-1;
		marquage[i]=false;
	}

	int a=position2int(ax,ay);
	int b=position2int(bx,by);

	C[a]=a;
	marquage[a]=true;

	File f; 
	f.ajouter(a);

	while ((marquage[b] == false) && (f.retourneNb() != 0))
	{
		k=f.enlever();

		nblibre=ToutePlusProcheLibre(k,t);

		for (nb=0; nb < nblibre; nb++)
		{
			l=t[nb];
			if (marquage[l] == false)
			{
				marquage[l]=true;
				f.ajouter(l);
				C[l]=k;
			}
		}

	}

	if (marquage[b])
	{
		// si l'affichage est demande, on va montrer le trajet a l'ecran
		if (affiche)
		{
			position e;
			i=b;
			while (C[i] != a)
			{
				i=C[i];
				e=int2position(i);
				move(e.x,e.y);
				affiche_symbole_joueur(id);
			}
		}
	}

	if (C[b] == -1)	return (false);
	else return (true);

}

#endif
