/*====================ÉCOLE POLYTECHNIQUE DE MONTRÉAL============================
  
	NOM DU FICHIER :glJeuWidget.cpp
	
	DESCRIPTION :	Déclaration de la classe gérant le QTWidget
	
	AUTEUR : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
	
===============================================================================*/

#include "qeventloop.h"
#include "qapplication.h"
#include "qpainter.h"
#include "qfont.h"
#include "qcolor.h"
#include "qtimer.h"

#include "glJeuWidget.h"

#include "CTerrain.h"
#include "CDepart.h"
#include "CTrou.h"
#include "CTrappe.h"
#include "CTroudeau.h"
#include "CBande.h"

//ENLEVER
#include <fstream>
using namespace std;

#include "al/al.h"
#include "al/alc.h"
#include "al/alut.h"

CPngTexture glJeuWidget::TexSky;
CPngTexture glJeuWidget::TexStars;
CPngTexture glJeuWidget::TexLune;
CPngTexture glJeuWidget::TexFlare;
CPngTexture glJeuWidget::TexShine;

/*------------------------------------------------------------------
  Fonction :	Widget::glJeuWidget
  
  Description :	Constructeur par paramètres
  
  Paramètres :
	 QWidget* parent : 
	 const char* nom): QGLWidget(parent : 
	 nom : 
	 
  Valeur retournée :	Aucune (constructeur) : 
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
glJeuWidget::glJeuWidget(QWidget* parent, const char* nom): QGLWidget(parent, nom)
{
    //Initialisation de toutes les variables
    Processing = false;
    Partie = 0;
    CalcTimer = 0;
    MessageTimer = 0;
    IdleTimer = 0;
    SoleilTimer = 0;
    Message = "";
    Mode3D = false;
    BallCam = false;
    Pers = 60;
    alpha = M_PI/2;
    beta = M_PI/4;
    eyex = 4-8* cos(alpha)*sin(beta);
    eyey = 4-8*sin(alpha)*sin(beta);
    eyez = 8*cos(beta);
    ax = 4;
    ay = 4;
    az = 0;
    lDepth[0]=0;
    AncientDepth[0] = AncientDepth[1] = 0;
    Police.InitialSetup("arial.ttf",30);
    PosSoleil[0] = 0.0;
    PosSoleil[1] =  7*(GRILLE_W>GRILLE_H?GRILLE_W:GRILLE_H);
    PosSoleil[2] =  0.0;
    AngleSoleil = 90.0;
    CouleurSoleil[0] = 1.0;
    CouleurSoleil[1] = 1.0;
    CouleurSoleil[2] = 0.0;
    CouleurSoleil[3] = 1.0;
    AlphaJourNuit = 0.0;
    CouleurAmbianteG = 1.0;
    CouleurAmbianteB = 1.0;
}

/*------------------------------------------------------------------
  Fonction :	
glJeuWidget::~glJeuWidget

  Description :	Destructeur
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
glJeuWidget::~glJeuWidget()
{
    TexSky.Unload();
    TexStars.Unload();
    TexLune.Unload();
    TexFlare.Unload();
    TexShine.Unload();
    CDepart::DechargeTextures();
    CTrou::DechargeTextures();
    CTrappe::DechargeTextures();
    CTroudeau::DechargeTextures();
    CTerrain::DechargeTextures();
    CBande::DechargeTextures();
    CPartie::DechargeTextures();
}

CPartie const* glJeuWidget::getPartie() const
{
    return this->Partie;
}

void glJeuWidget::setPartie(CPartie* P)
{
    this->Partie = P;
}

bool glJeuWidget::getMode3d() const
{
    return this->Mode3D;
}
/*------------------------------------------------------------------
  Fonction :	initializeGL
  
  Description :	Initialise le widget OpenGL
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::initializeGL()
{
    glEnable(GL_BLEND);
    //Test nécessaire pour le mode 3D
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glClearStencil(0x0);
    
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_LINE_SMOOTH);
    TexSky.LoadPNG("editeur/images/sky.png");
    TexStars.LoadPNG("editeur/images/stars.png");
    TexLune.LoadPNG("editeur/images/lune.png");
    TexFlare.LoadPNG("editeur/images/flare.png");
    TexShine.LoadPNG("editeur/images/shine.png");
    CPartie::ChargeSons();
    CTroudeau::ChargeTextures();
    CPartie::ChargeTextures();
    CDepart::ChargeTextures();
    CTrou::ChargeTextures();
    CTerrain::ChargeTextures();
    CTrappe::ChargeTextures();
    CBande::ChargeTextures();
    //S'il y a plus d'un joueur, on annonce à qui est le tour
    if (Partie->getJoueurs().size() != 1)
    {
	setMessage(QString(Partie->getJoueurs()[Partie->getJoueurCourant()-1].getNom()) + QString(", c'est a toi de jouer!"));
	StartMessageTimer();
    } else {
	Partie->setEtatJeu(ETAT_PLACEMENTBALLE);
    }
    
    int Interval = rand()%10000+5000;
    BirdSoundTimer=startTimer(Interval);
    
    IdleTimer=startTimer(0);
    
    SoleilTimer = startTimer(30); 
    
    //modèle d'illumination
    GLfloat mod_ambient[] = { 0.1, 0.1, 0.1, 1.0};
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, mod_ambient);
    glShadeModel(GL_SMOOTH);
    
    //parametres pour lumieres du phare 2 et 3
    GLfloat lumPhare_ambiante[] = {0.2, 0.2, 0.2, 1.0};
    GLfloat lumPhare_diffuse[] = {1.0, 1.0, 1.0, 1.0};
    GLfloat lumPhare_speculaire[] = {1.0, 1.0, 1.0,1.0};
    GLfloat const_attenuation = 0.0075f;
    GLfloat linear_attenuation = 0.0025f;
    GLfloat quad_attenuation = 0.001f;
    GLfloat spot_exponent = 20.0f;
    GLfloat spot_cut_off = 45.0f;
    
    //lumiere du phare 2
    glLightfv(GL_LIGHT2, GL_AMBIENT, lumPhare_ambiante);
    glLightfv(GL_LIGHT2, GL_DIFFUSE, lumPhare_diffuse);
    glLightfv(GL_LIGHT2, GL_SPECULAR, lumPhare_speculaire);
    glLightf(GL_LIGHT2, GL_SPOT_CUTOFF, spot_cut_off);
    glLightf(GL_LIGHT2, GL_SPOT_EXPONENT, spot_exponent);
    glLightf(GL_LIGHT2, GL_CONSTANT_ATTENUATION, const_attenuation);
    glLightf(GL_LIGHT2, GL_LINEAR_ATTENUATION, linear_attenuation);
    glLightf(GL_LIGHT2, GL_QUADRATIC_ATTENUATION, quad_attenuation);
	
    //lumiere du phare 3
    glLightfv(GL_LIGHT3, GL_AMBIENT, lumPhare_ambiante);
    glLightfv(GL_LIGHT3, GL_DIFFUSE, lumPhare_diffuse);
    glLightfv(GL_LIGHT3, GL_SPECULAR, lumPhare_speculaire);
    glLightf(GL_LIGHT3, GL_SPOT_CUTOFF, spot_cut_off);
    glLightf(GL_LIGHT3, GL_SPOT_EXPONENT, spot_exponent);  
    glLightf(GL_LIGHT3, GL_CONSTANT_ATTENUATION, const_attenuation);
    glLightf(GL_LIGHT3, GL_LINEAR_ATTENUATION, linear_attenuation);
    glLightf(GL_LIGHT3, GL_QUADRATIC_ATTENUATION, quad_attenuation);
       
    //activation de la lumière du soleil et lune
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHT1);  
    glEnable(GL_LIGHTING);
    
    SkyAngle1 = rand()%360;
    SkyAngle2 = rand()%360;
    SpeedAngle1 = (rand()%300)/1000.0f+0.05f;
    SpeedAngle2 = (rand()%300)/1000.0f+0.05f;
}

/*------------------------------------------------------------------
  Fonction :	resizeGL
  
  Description :	Gère les redimensionnements du widget
  
  Paramètres : int w : nouvelle largeur
			   int h : nouvelle hauteur
			   
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::resizeGL(int w, int h)
{
    glViewport(0,0,w,h);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (!Mode3D)
	gluOrtho2D(-1.0f, GRILLE_W+1.0f, -1.0f, GRILLE_H+1.0f);
    else
	gluPerspective(60,(float)w/(float)h,1,100);
    
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/*------------------------------------------------------------------
  Fonction :	resizeGL
  
  Description :	Gère les redimensionnements "forces" du widget
  
  Paramètres : int w : nouvelle largeur
	       int h : nouvelle hauteur
	       int delta : incrementation ou decrementation de l'angle du gluPerspective
			   
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::resizeGL(int w, int h, int delta)
{
    glViewport(0,0,w,h);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if(Mode3D)
	gluPerspective(delta, (float)w/(float)h, 1, 100);
    else
	gluOrtho2D(-1.0f, GRILLE_W+1.0f, -1.0f, GRILLE_H+1.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/*------------------------------------------------------------------
  Fonction :	paintGL
  
  Description :	Affichage du widget
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::paintGL()
{
    glClearColor(0.3, 0.4, 1.0, 1.0);
    Partie->getTerrain()->getTrou()->setStencil();
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    if (Mode3D)
    {
	glLoadIdentity();
	CVector3f BallPos = Partie->getTerrain()->getBalle()->getPos();
	CVector3f DepPos = Partie->getTerrain()->getDepart()->getPos();
	//Ajuster la camera en fonction de l'incrementation ou de la decrementation des angles de vue (alpha et beta)
	if (!BallCam)
	{    
	    gluLookAt(eyex, eyey, eyez, 
		      ax,ay,0,
		      0,0,1);
	} else {
	    //Suivre la balle si le joueur choisit l'option "Camera sur la balle"
	    if (Partie->getEtatJeu() != ETAT_PLACEMENTBALLE)
	    {
		gluLookAt(eyex, eyey, eyez, 
			  BallPos.GetPosX(),BallPos.GetPosY(),BallPos.GetPosZ(),
			  0,0,1);
	    } else {
		gluLookAt(eyex, eyey, eyez, 
			  DepPos.GetPosX(),DepPos.GetPosY(),DepPos.GetPosZ(),
			  0,0,1);
	    }
	}
    }
    if(Partie)
      {
	//<------ DÉBUT ÉCLAIRAGE-----
	GLfloat lum_position[] = {PosSoleil[0], PosSoleil[1], PosSoleil[2], 0.0};
	//création des sources de lumière
	GLfloat lum_ambiante[] = { 0.9*(1-AlphaJourNuit), 0.9*CouleurAmbianteG*(1-AlphaJourNuit), 0.9*CouleurAmbianteB*(1-AlphaJourNuit), 1.0};
	GLfloat lum_diffuse[] = {1.0*(1-AlphaJourNuit), 1.0*CouleurAmbianteG*(1-AlphaJourNuit), 1.0*CouleurAmbianteB*(1-AlphaJourNuit), 1.0};
	GLfloat lum_speculaire[] = {1.0*(1-AlphaJourNuit), 1.0*(1-AlphaJourNuit), 1.0*(1-AlphaJourNuit), 1.0};
	glLightfv(GL_LIGHT0, GL_AMBIENT, lum_ambiante);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, lum_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, lum_speculaire);
	
	//rotation de la lumiere 0 : le soleil
	glPushMatrix();
	glRotatef(180,0,0,1);
	glRotatef(AngleSoleil, 1.0, 0.0, 0.0);
	glLightfv(GL_LIGHT0, GL_POSITION, lum_position);
	glPopMatrix();
	
	//parametres de la lumiere 1 : lune
	GLfloat lune_ambiante[] = { 0.2*AlphaJourNuit, 0.2*AlphaJourNuit, 0.2*AlphaJourNuit, 1.0};
	GLfloat lune_diffuse[] = {0.5*AlphaJourNuit, 0.5*AlphaJourNuit, 0.5*AlphaJourNuit, 1.0};
	GLfloat lune_speculaire[] = {1.0*AlphaJourNuit, 1.0*AlphaJourNuit, 1.0*AlphaJourNuit, 1.0};
	glLightfv(GL_LIGHT1, GL_AMBIENT, lune_ambiante);
	glLightfv(GL_LIGHT1, GL_DIFFUSE, lune_diffuse);
	glLightfv(GL_LIGHT1, GL_SPECULAR, lune_speculaire);
	
	//rotation de la lumiere 1 : la lune
	glPushMatrix();
	glRotatef(180,0,0,1);
	glRotatef(AngleSoleil + 180.0f, 1.0, 0.0, 0.0);
	glLightfv(GL_LIGHT1, GL_POSITION, lum_position);
	glPopMatrix();
	
	Partie->getTerrain()->AfficheLumierePhare();
	//------ FIN ÉCLAIRAGE ----->
	
	//SkyBox
	glEnable(GL_TEXTURE_2D);
	//Stars if sun down
	GLfloat fond_diffuse[] = {1.0, 1.0, 1.0, AlphaJourNuit};
	GLfloat fond_specular[] = {0.1, 0.1, 0.1, 1.0};
	GLfloat fond_shininess[] = {1.0};
	GLfloat fond_ambient[] = { 1.0, 1.0, 1.0, 1.0 };
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, fond_ambient);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, fond_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fond_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, fond_shininess);
	glBindTexture(GL_TEXTURE_2D, TexStars.GetID());
	GLUquadricObj* StarsBox = gluNewQuadric();
	gluQuadricTexture(StarsBox, GL_TEXTURE_2D);
	gluQuadricOrientation(StarsBox, (GLenum)GLU_INSIDE);
	gluSphere(StarsBox, 8*(GRILLE_W>GRILLE_H?GRILLE_W:GRILLE_H), 20, 20);
	gluDeleteQuadric(StarsBox);
	glDisable(GL_TEXTURE_2D);
	//SOLEIL ET LUNE ENTRE LES ETOILES ET LES NUAGES
	AfficheSoleil();
	AfficheLune();
	
	glEnable(GL_TEXTURE_2D);
	//Clouds
	GLfloat sky_diffuse[] = {1.0, 1.0, 1.0, 1.0};
	GLfloat sky_specular[] = {0.1, 0.1, 0.1, 1.0};
	GLfloat sky_shininess[] = {1.0};
	GLfloat sky_ambient[] = { 1.0, 1.0, 1.0, 1.0 };
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, sky_ambient);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, sky_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, sky_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, sky_shininess);
	glPushMatrix();
	glRotatef(SkyAngle1,1,0,0);
	glRotatef(SkyAngle2,0,1,0);
	glBindTexture(GL_TEXTURE_2D, TexSky.GetID());
	GLUquadricObj* SkyBox = gluNewQuadric();
	gluQuadricTexture(SkyBox, GL_TEXTURE_2D);
	gluQuadricOrientation(SkyBox, (GLenum)GLU_INSIDE);
	gluSphere(SkyBox, 6*(GRILLE_W>GRILLE_H?GRILLE_W:GRILLE_H), 100, 100);
	gluDeleteQuadric(SkyBox);
	glPopMatrix();
	glDisable(GL_TEXTURE_2D);
	//Fin SkyBox
	
	Partie->AfficheGL();
	
	AfficheFlare();
	
	//Affichage du message au centre de l'écran
	if (Message != "" || Partie->getEtatJeu() == ETAT_REPLAY)
	{
	  glDisable(GL_LIGHTING);
	  glDepthFunc(GL_ALWAYS);

	    glMatrixMode(GL_PROJECTION);
	    glPushMatrix();
	    glLoadIdentity();
	    gluOrtho2D(0,width(),height(),0);
	    glMatrixMode(GL_MODELVIEW);
	    glPushMatrix();
	    glLoadIdentity();
	    if (Partie->getEtatJeu() == ETAT_REPLAY)
	    {
		int TxtWidth = Police.GetTextWidth("Reprise");
		int TxtHeight = Police.GetHeight();
		glColor3f(0,0,0);
		Police.glDrawText((width()-TxtWidth)/2.0f+1,30+1,"Reprise");
		glColor3f(1,0,0);
		Police.glDrawText((width()-TxtWidth)/2.0f,30,"Reprise");
	    } else {
		int TxtWidth = Police.GetTextWidth(Message.ascii());
		int TxtHeight = Police.GetHeight();
		glColor3f(0,0,0);
		Police.glDrawText((width()-TxtWidth)/2.0f+1,(height()-TxtHeight)/2.0f+1,Message);
		glColor3f(1,0.5,0);
		Police.glDrawText((width()-TxtWidth)/2.0f,(height()-TxtHeight)/2.0f,Message);
	    }
	    glColor3f(1,1,1);
	    glPopMatrix();
	    glMatrixMode(GL_PROJECTION);
	    glPopMatrix();
	    glMatrixMode(GL_MODELVIEW);
	    glDepthFunc(GL_LEQUAL);
	    glEnable(GL_LIGHTING);
	}
    }
}

/*------------------------------------------------------------------
  Fonction : glJeuWidget::AfficheSoleil
  
  Description :	Fonction qui affiche le soleil
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::AfficheSoleil()
{
    glDisable(GL_LIGHTING);

    glPushMatrix( );
    glRotatef(180,0,0,1);
    glRotatef(AngleSoleil, 1.0, 0.0, 0.0);
    glTranslatef( PosSoleil[0], PosSoleil[1], PosSoleil[2] );
    glColor4f(CouleurSoleil[0], CouleurSoleil[1], CouleurSoleil[2], CouleurSoleil[3]);

    GLUquadricObj* Sphere = gluNewQuadric();   
    gluSphere(Sphere, 2.0, 30,30);
    gluDeleteQuadric(Sphere);
    glPopMatrix();    
    glEnable(GL_LIGHTING);
}

/*------------------------------------------------------------------
  Fonction : glJeuWidget::AfficheFlare
  
  Description :	Affiche un petit flare à la potision du soleil
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::AfficheFlare()
{
    glDisable(GL_LIGHTING);
    float lDepth[1];
    GLdouble winx, winy, winz;
    GLdouble lModelMatrix[16];
    GLdouble lProjMatrix[16];	
    GLint lViewPort[4];
    float TmpPos[3] = {0,0,0};
    float Color[3];
    
    glPushMatrix();
    //Calcul de la vraie position du soleil
    TmpPos[0] = PosSoleil[0];
    TmpPos[1] = cos(AngleSoleil*M_PI/180)*PosSoleil[1]-sin(AngleSoleil*M_PI/180)*PosSoleil[2];
    TmpPos[2] = sin(AngleSoleil*M_PI/180)*PosSoleil[1]+cos(AngleSoleil*M_PI/180)*PosSoleil[2];
    TmpPos[0] = -TmpPos[0];
    TmpPos[1] = -TmpPos[1];
    
    glGetDoublev( GL_MODELVIEW_MATRIX, lModelMatrix );
    glGetDoublev( GL_PROJECTION_MATRIX, lProjMatrix );
    glGetIntegerv( GL_VIEWPORT, lViewPort );
    
    if (gluProject(TmpPos[0], TmpPos[1], TmpPos[2], lModelMatrix, lProjMatrix, lViewPort, &winx, &winy, &winz))
    {
	//Couleur du pixel à l'emplacement du soleil afin de vérifier qu'il n'est pas caché par un objet
	glReadPixels(winx,winy,1,1,GL_RGB,GL_FLOAT,&Color);
	if (winx > lViewPort[0] && winx < lViewPort[2] &&
	    winy > lViewPort[1] && winy < lViewPort[3] &&
	    winz > 0 &&
	    fabs(Color[0] - CouleurSoleil[0]) < 0.05f &&
	    fabs(Color[1] - CouleurSoleil[1]) < 0.05f &&
	    fabs(Color[2] - CouleurSoleil[2]) < 0.05f)
	{
	    float Dist = (winx - (float)lViewPort[2]/2)*(winx - (float)lViewPort[2]/2)+
			 (winy- (float)lViewPort[3]/2)*(winy- (float)lViewPort[3]/2);
	    float MaxDist = ((float)lViewPort[2]/2)*((float)lViewPort[2]/2)+((float)lViewPort[3]/2)*((float)lViewPort[3]/2);
	    glMatrixMode(GL_PROJECTION);
	    glPushMatrix();
	    glLoadIdentity();
	    gluOrtho2D(0,width(),0,height());
	    glMatrixMode(GL_MODELVIEW);
	    glPushMatrix();
	    glLoadIdentity();
	    
	    glDisable(GL_DEPTH_TEST);
	    glEnable(GL_TEXTURE_2D);
	    //Flare
	    glBindTexture(GL_TEXTURE_2D, TexFlare.GetID());
	    glColor4f(CouleurSoleil[0], CouleurSoleil[1], CouleurSoleil[2], 1-2.5f*(Dist/MaxDist));
	    glBegin(GL_QUADS);
	    {
		glTexCoord2i(0,1); glVertex2f(winx-40,winy-40);
		glTexCoord2i(1,1); glVertex2f(winx+40,winy-40);
		glTexCoord2i(1,0); glVertex2f(winx+40,winy+40);
		glTexCoord2i(0,0); glVertex2f(winx-40,winy+40);
	    }
	    glEnd();
	    
	    //Shine
	    glColor4f(CouleurSoleil[0], CouleurSoleil[1], CouleurSoleil[2], 1-(Dist/MaxDist));
	    glBindTexture(GL_TEXTURE_2D, TexShine.GetID());
	    glBegin(GL_QUADS);
	    {
		glTexCoord2i(0,1); glVertex2f(winx-100,winy-100);
		glTexCoord2i(1,1); glVertex2f(winx+100,winy-100);
		glTexCoord2i(1,0); glVertex2f(winx+100,winy+100);
		glTexCoord2i(0,0); glVertex2f(winx-100,winy+100);
	    }
	    glEnd();
	    glDisable(GL_TEXTURE_2D);   
	    glEnable(GL_DEPTH_TEST);
	    
	    glPopMatrix();
	    glMatrixMode(GL_PROJECTION);
	    glPopMatrix();
	    glMatrixMode(GL_MODELVIEW);
	}
    }
    glPopMatrix();
    glEnable(GL_LIGHTING);
}

/*------------------------------------------------------------------
  Fonction : glJeuWidget::AfficheLune
  
  Description :	Fonction qui affiche la Lune
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::AfficheLune()
{
    glDisable(GL_LIGHTING);    
    glPushMatrix( );
    
    glRotatef(180,0,0,1);
    glRotatef(AngleSoleil+180.0f, 1.0, 0.0, 0.0);
    glTranslatef(0.0f,  7*(GRILLE_W>GRILLE_H?GRILLE_W:GRILLE_H), 0.0f );
    glRotatef(AngleSoleil+90.0f, -1.0, 0.0, 0.0);
    glColor4f(1.0, 1.0, 1.0, 1.0);
    
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, TexLune.GetID());
    glBegin(GL_QUADS);
    {
	glTexCoord2i(0,1); glVertex2f(-3,-3);
	glTexCoord2i(1,1); glVertex2f(3,-3);
	glTexCoord2i(1,0); glVertex2f(3,3);
	glTexCoord2i(0,0); glVertex2f(-3,3);
    }
    glEnd();
    glDisable(GL_TEXTURE_2D);
    
    glPopMatrix();
    glEnable(GL_LIGHTING);
}
/*------------------------------------------------------------------
  Fonction :	StartCalcTimer
  
  Description :	Départ du timer de calculs pour les collisions
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::StartCalcTimer()
{
    if (CalcTimer != 0)
	killTimer(CalcTimer);   
    
    this->CalcTimer = startTimer(TIMER_INTERVAL);
}


/*------------------------------------------------------------------
  Fonction :	StopCalcTimer
  
  Description :	Arrête le timer pour les calculs des collisions
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::StopCalcTimer()
{
    if (CalcTimer != 0)
    {
	this->killTimer(this->CalcTimer);
	this->CalcTimer = 0;
    }
}

/*------------------------------------------------------------------
  Fonction :	StartMessageTimer
  
  Description :	Départ du timer pour afficher le joueur courant
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::StartMessageTimer()
{
    if (MessageTimer != 0)
	killTimer(MessageTimer);   
    
    this->MessageTimer = startTimer(2000);
    
    Partie->setEtatJeu(ETAT_INACTIF);
}

/*------------------------------------------------------------------
  Fonction :	StopMessageTimer
  
  Description :	Arrête le timer d'affichage du joueur courant
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::StopMessageTimer()
{
    if (MessageTimer != 0)
    {
	this->killTimer(this->MessageTimer);
	this->MessageTimer = 0;
    }
    
    Message = "";
    Partie->setEtatJeu(ETAT_PLACEMENTBALLE);
}

/*------------------------------------------------------------------
  Fonction :	setMessage
  
  Description :	Définit le message à afficher (joueur courant)
  
  Paramètres : const QString& Mes : Message à afficher
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::setMessage(const QString& Mes)
{
    Message = Mes;
}

/*------------------------------------------------------------------
  Fonction :	timerEvent
  
  Description :	Événement appelé à chaque interval des timers
  
  Paramètres : QTimerEvent * T : Permet de connaître le timer ayant appelé la fonction timerEvent
						
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::timerEvent(QTimerEvent * T)
{
    //Timer des calculs de la balle
    if (T->timerId() == CalcTimer)
    {
	int i;
	for(i = 0; i<NB_CALCS; i++)
	{
	    Partie->getTerrain()->CCM(Partie->getTerrain()->getBalle());
	    Partie->getTerrain()->CollisionObjets(Partie->getTerrain()->getBalle());
	    Partie->getTerrain()->getBalle()->Avance();
	}
	//Si la vitesse devient trop petite
	if(Partie->getTerrain()->getBalle()->StopBall())
	{
	    StopCalcTimer();
	    if (Partie->getEtatJeu() == ETAT_CALCULS)
		Partie->setEtatJeu(ETAT_INACTIF);
	    emit BallStoped();
	}
    }
    
    //Timer pour l'affichage des messages
    else if (T->timerId() == MessageTimer) 
    {
	StopMessageTimer();
    }
    
    //Timer pour les sons d'oiseaux
    else if (T->timerId() == BirdSoundTimer) 
    {
	Partie->PlayBirdSound();
	killTimer(BirdSoundTimer);
	int Interval;
	Interval = rand()%10000+5000;
	BirdSoundTimer=startTimer(Interval);
	
    }
    
    //Timer pour les animations
    else if (T->timerId() == IdleTimer) 
    {
	Partie->UpdateAnimations();
    } 
    
    //Timer pour le jour et la nuit
    else if (T->timerId() == SoleilTimer) 
    {
	if (AngleSoleil > 165.0f && AngleSoleil < 360.0f)
	    Partie->getTerrain()->TournerPhare();
	SkyAngle1 += SpeedAngle1;
	SkyAngle2 += SpeedAngle2;
	AngleSoleil += 0.18f;
	if (AngleSoleil > 360.0f) AngleSoleil-=360.0f;
	
	//soleil passe de jaune a rouge : coucher soleil
	if (AngleSoleil > 165.0f && AngleSoleil < 190.0f)
	{
	    CouleurSoleil[1] = 1.0f - (AngleSoleil-165.0f)/25.0f;
	    CouleurAmbianteG = (1.0f - (AngleSoleil-165.0f)/50.0f);//couleur ambiante pas trop rouge
	    CouleurAmbianteB = 1.0f - (AngleSoleil-165.0f)/25.0f;
	    glEnable(GL_LIGHT2);
	    glEnable(GL_LIGHT3);
	}
	//soleil passe de rouge a jaune : lever soleil
	else if (AngleSoleil > 350.0f && AngleSoleil < 360.0f)
	{
	    CouleurSoleil[1] = (AngleSoleil-350.0f)/25.0;
	    CouleurAmbianteG = 0.5 + (AngleSoleil-350.0f)/50.0f;//couleur ambiante pas trop rouge
	    CouleurAmbianteB = (AngleSoleil-350.0f)/25.0;
	}
	//idem
	else if (AngleSoleil >=0.0f && AngleSoleil < 15.0f)
	{
	    CouleurSoleil[1] = (AngleSoleil+10.0f)/25.0f;
	    CouleurAmbianteG =0.5 + (AngleSoleil +10.0f)/50.0f;//couleur ambiante pas trop rouge
	    CouleurAmbianteB = (AngleSoleil+10.0f)/25.0f;
	    glDisable(GL_LIGHT2);
	    glDisable(GL_LIGHT3);
	}
	//activer lentement la lune et desactiver lentement le soleil
	//faire apparaitre lentement les etoiles
	else if (AngleSoleil >=190.0f && AngleSoleil < 210.0f )
	{
	    AlphaJourNuit = (AngleSoleil -190.0f)/20.0f;
	}
	//activer lentement le soleil et desactiver lentement la  lune
	//faire disparaitre lentement les etoiles
	else if (AngleSoleil > 330.0f && AngleSoleil <= 350.0f)
	{
	    AlphaJourNuit = 1.0f - (AngleSoleil-330.0f)/20.0f;
	}
    }
    updateGL();
}

//Define necessaires aux mousePressEvent, mouseReleaseEvent, mouseMoveEvent
#define MOUSE_X ((float) ((e->x() / (float)width()) * ((float)GRILLE_W + 2.0f) - 1))
#define MOUSE_Y (GRILLE_H+2.0f) - (float) (((e->y() / (float) height()) * (GRILLE_H+2.0f) + 1))

/*------------------------------------------------------------------
  Fonction :	mousePressEvent
  
  Description :	Événement appelé aux appuis des boutons de souris
  
  Paramètres : QMouseEvent * e : Information sur la souris
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::mousePressEvent(QMouseEvent * e)
{
    makeCurrent();
    if(Mode3D)
    {
	//Ajuster la position de la souris en fonction de la position "vue" a l'ecran
	float lDepth[1];
	GLdouble Pos[3];
	GLdouble lModelMatrix[16];
	GLdouble lProjMatrix[16];	
	GLint lViewPort[4];
	
	glGetDoublev( GL_MODELVIEW_MATRIX, lModelMatrix );
	glGetDoublev( GL_PROJECTION_MATRIX, lProjMatrix );
	glGetIntegerv( GL_VIEWPORT, lViewPort );
	
	// Get the Z coordinate for the pixel location
	glReadPixels(e->x(), lViewPort[3] - e->y(),		
		     1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, lDepth);
	gluUnProject(e->x(), lViewPort[3] - e->y(),	
		     lDepth[0], lModelMatrix, lProjMatrix, lViewPort,
		     &Pos[0], &Pos[1], &Pos[2]);
	emit MouseClick(e->button(), BUTTON_DOWN, Pos[0], Pos[1]);
    }
    else
    {
	emit MouseClick(e->button(), BUTTON_DOWN, MOUSE_X, MOUSE_Y);
    }
}

/*------------------------------------------------------------------
  Fonction :	mouseReleaseEvent
  
  Description :	Événement appelé aux relâchements des boutons de souris
  
  Paramètres : QMouseEvent * e : Information sur la souris
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::mouseReleaseEvent(QMouseEvent * e)
{
    makeCurrent();
    if(Mode3D)
    {
	//Ajuster la position de la souris en fonction de la position "vue" a l'ecran
	float lDepth[1];
	GLdouble Pos[3];
	GLdouble lModelMatrix[16];
	GLdouble lProjMatrix[16];	
	GLint lViewPort[4];
	
	glGetDoublev( GL_MODELVIEW_MATRIX, lModelMatrix );
	glGetDoublev( GL_PROJECTION_MATRIX, lProjMatrix );
	glGetIntegerv( GL_VIEWPORT, lViewPort );
	
	// Get the Z coordinate for the pixel location
	glReadPixels(e->x(), lViewPort[3] - e->y(),		
		     1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, lDepth);
	gluUnProject(e->x(), lViewPort[3] - e->y(),	
		     lDepth[0], lModelMatrix, lProjMatrix, lViewPort,
		     &Pos[0], &Pos[1], &Pos[2]);
	emit MouseClick(e->button(), BUTTON_UP, Pos[0], Pos[1]);
    }
    else
    {
	emit MouseClick(e->button(), BUTTON_UP, MOUSE_X, MOUSE_Y);
    }
}

/*------------------------------------------------------------------
  Fonction :	mouseMoveEvent
  
  Description :	Événement appelé aux déplacements des boutons de souris
  
  Paramètres : QMouseEvent * e : Information sur la souris
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::mouseMoveEvent(QMouseEvent * e)
{
    if (!Processing)
    {
	makeCurrent();
	if(Mode3D)
	{
	    GLdouble Pos[3];
	    GLdouble lModelMatrix[16];
	    GLdouble lProjMatrix[16];	
	    GLint lViewPort[4];
	    
	    glGetDoublev( GL_MODELVIEW_MATRIX, lModelMatrix );
	    glGetDoublev( GL_PROJECTION_MATRIX, lProjMatrix );
	    glGetIntegerv( GL_VIEWPORT, lViewPort );
	    
	    //Redessiner le plan pour que la fonction glReadPixels trouve la bonne profondeur et ne soit
	    // pas melee par les obstacles
	    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	    glBegin(GL_QUADS);
	    glVertex2f(-2*GRILLE_W, -2*GRILLE_H);
	    glVertex2f(-2*GRILLE_W, 3*GRILLE_H);
	    glVertex2f(3*GRILLE_W, 3*GRILLE_H);
	    glVertex2f(3*GRILLE_W, -2*GRILLE_W);
	    glEnd();
	    // Get the Z coordinate for the pixel location
	    AncientDepth[0] = lDepth[0];
	    AncientDepth[1] = AncientDepth[0];
	    glReadPixels(e->x(), lViewPort[3] - e->y(),		
			 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, lDepth);
	    gluUnProject(e->x(), lViewPort[3] - e->y(),	
			 lDepth[0], lModelMatrix, lProjMatrix, lViewPort,
			 &Pos[0], &Pos[1], &Pos[2]);
	    emit MouseMove(e->state(), Pos[0], Pos[1]);
	}
	else
	{
	    emit MouseMove(e->state(), MOUSE_X, MOUSE_Y);
	}
	Processing = true;
	qApp->eventLoop()->processEvents(QEventLoop::AllEvents,10);
	Processing = false;
    }
}

/*------------------------------------------------------------------
  Fonction :	wheelEvent
  
  Description :	Événement appelé avec la roulette de la souris
  
  Paramètres : QWheelEvent * e : Information sur la roulette
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::wheelEvent(QWheelEvent * e)
{
    emit MouseRoll(e->delta());
}

/*------------------------------------------------------------------
  Fonction :	keyPressEvent
  
  Description :	Événement appelé aux appuis des touches du clavier
  
  Paramètres : QKeyEvent * e : Information sur le clavier
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::keyPressEvent(QKeyEvent * e)
{
    if(Mode3D)
    {
	emit KeyPress(e->key(), KEYDOWN);
    }
}

/*------------------------------------------------------------------
  Fonction :	keyReleaseEvent
  
  Description :	Événement appelé aux relâchements des touches du clavier
  
  Paramètres : QKeyEvent * e : Information sur le clavier
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::keyReleaseEvent(QKeyEvent * e)
{
    emit KeyPress(e->key(), KEYUP);    
}

/*------------------------------------------------------------------
  Fonction :	getMessage
  
  Description :	Retourne le message à afficher actuellement
  
  Retour : const QString& : Le message à afficher
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
const QString& glJeuWidget::getMessage()
{
    return Message;
}

/*------------------------------------------------------------------
  Fonction :	ChangeMode
  
  Description :	Changement entre le mode 2D et le mode 3D
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::ChangeMode()
{
    Mode3D = !Mode3D;
    resizeGL(width(),height());
    updateGL();
}

/*------------------------------------------------------------------
  Fonction :	getMode3D
  
  Description :	Retourne si l'affichage 3D est actif
  
  Retour : bool : Vrai si l'affichage est en 3D
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
bool glJeuWidget::getMode3D() const
{
    return Mode3D;
}

/*------------------------------------------------------------------
  Fonction :	setBallCam
  
  Description :	Permet de fixer la caméra sur la balle
  
  Paramètres : 	bool BallCam : Vrai si la caméra doit suivre la balle
  
  Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::setBallCam(bool BallCam)
{
    this->BallCam = BallCam;
}

/*------------------------------------------------------------------
  Fonction :	PanRight()
  
  Description :	Fonction pour tourner de facon horaire autour d'un point fixe
  
    Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::PanRight()
{
    alpha -= M_PI/20;
    	    eyex = ax-8*cos(alpha)*sin(beta);
	    eyey = ay-8*sin(alpha)*sin(beta);
	    eyez = 8*cos(beta);
}

/*------------------------------------------------------------------
  Fonction :	PanLeft()
  
  Description :	Fonction pour tourner de facon antihoraire autour d'un point fixe
  
    Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::PanLeft()
{
    alpha += M_PI/20;
    	    eyex = ax-8*cos(alpha)*sin(beta);
	    eyey = ay-8*sin(alpha)*sin(beta);
	    eyez = 8*cos(beta);
}

/*------------------------------------------------------------------
  Fonction :	PanDown()
  
  Description :	Fonction pour descendre la camera par rapport a un point fixe
  
    Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::PanDown()
{
    if(beta < M_PI/2 - 2*M_PI/40 && beta >= 0)
	beta += M_PI/40;
    	    eyex = ax-8*cos(alpha)*sin(beta);
	    eyey = ay-8*sin(alpha)*sin(beta);
	    eyez = 8*cos(beta);
}

/*------------------------------------------------------------------
  Fonction :	PanUp()
  
  Description :	Fonction pour elever la camera au-dessus d'un point fixe
  
    Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::PanUp()
{
    if(beta <= M_PI/2 - 2*M_PI/40 && beta > M_PI/40)
	beta -= M_PI/40;
	    eyex = ax-8*cos(alpha)*sin(beta);
	    eyey = ay-8*sin(alpha)*sin(beta);
	    eyez = 8*cos(beta);
}

/*------------------------------------------------------------------
  Fonction :	PanIn()
  
  Description :	Fonction pour faire un zoom in
  
    Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::PanIn()
{
    if(Pers >= 25)
	Pers -= 2;
    resizeGL(width(), height(), Pers);
}

/*------------------------------------------------------------------
  Fonction :	PanIn()
  
  Description :	Fonction pour faire un zoom out
  
    Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::PanOut()
{
    if(Pers <= 85)
	Pers += 2;
    resizeGL(width(), height(), Pers);    
}

/*------------------------------------------------------------------
  Fonction :	StrafeLeft()
  
  Description :	Fonction pour se déplacer vers la gauche
  
    Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::StrafeLeft()
{
    tempx = ax;
    tempy = ay;
    CVector3f look(-ay+eyey,ax-eyex, 0);
    look = 0.5*look.GetNormalVector();
    tempx += look.GetPosX();
    tempy += look.GetPosY();
    if(tempx >= -GRILLE_W && tempx <= 2*GRILLE_W)
    {
	if(tempy  >= -GRILLE_H && tempy <= 2*GRILLE_H)
	{
	    ax = tempx;
	    ay = tempy;
	    eyex += look.GetPosX();
	    eyey += look.GetPosY();
	}
    }    
}

/*------------------------------------------------------------------
  Fonction :	StrafeRight()
  
  Description :	Fonction pour se déplacer vers la droite
  
    Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::StrafeRight()
{  
    tempx = ax;
    tempy = ay;
    CVector3f look(ay-eyey,-(ax-eyex), 0);
    look = 0.5*look.GetNormalVector();
    tempx += look.GetPosX();
    tempy += look.GetPosY();
    if(tempx >= -GRILLE_W && tempx <= 2*GRILLE_W)
    {
	if(tempy  >= -GRILLE_H && tempy <= 2*GRILLE_H)
	{
	    ax = tempx;
	    ay = tempy;
	    eyex += look.GetPosX();
	    eyey += look.GetPosY();
	}
    }
}

/*------------------------------------------------------------------
  Fonction :	StrafeDown()
  
  Description :	Fonction pour se déplacer vers par en arrière
  
    Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::StrafeDown()
{
    tempx = ax;
    tempy = ay;
    CVector3f look(ax-eyex, ay-eyey, 0);
    look = 0.5*look.GetNormalVector();
    tempx -= look.GetPosX();
    tempy -= look.GetPosY();
    if(tempx >= -GRILLE_W && tempx <= 2*GRILLE_W)
    {
	if(tempy  >= -GRILLE_H && tempy <= 2*GRILLE_H)
	{
	    ax = tempx;
	    ay = tempy;
	    eyex -= look.GetPosX();
	    eyey -= look.GetPosY();
	}
    }
}

/*------------------------------------------------------------------
  Fonction :	StrafeUp()
  
  Description :	Fonction pour se déplacer par en avant
  
    Par : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  
------------------------------------------------------------------*/
void glJeuWidget::StrafeUp()
{       
    tempx = ax;
    tempy = ay;
    CVector3f look(ax-eyex, ay-eyey, 0);
    look = 0.5*look.GetNormalVector();
    tempx += look.GetPosX();
    tempy += look.GetPosY();
    if(tempx >= -GRILLE_W && tempx <= 2*GRILLE_W)
    {
	if(tempy  >= -GRILLE_H && tempy <= 2*GRILLE_H)
	{
	    ax = tempx;
	    ay = tempy;
	    eyex += look.GetPosX();
	    eyey += look.GetPosY();
	}
    }
}

