#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdexcept>
#include <string>
#include <vector>
#include <math.h>

using namespace std;

#include "Moteur.h"
#include "sdlglutils.h"

//Fonctions de la classe CMoteur ...

CMoteur::CMoteur()
{
	//Un chti constructeur ...
	//On initialise quelques variables, histoire de ...
	Screen = NULL;
	nbFPS = 0;
	LastFPS = 0;
	FPS = 0;

}

CMoteur::~CMoteur()
{
	//Et un chti destructeur ...

}

int CMoteur::Init(bool FullScreen, const char *TitreFenetre)
{
	SDL_Init(SDL_INIT_VIDEO);
	SDL_WM_SetCaption(TitreFenetre ,NULL);
	IsFullScreen = FullScreen;

	if(FullScreen)
		Screen = SDL_SetVideoMode(0, 0, 32, SDL_OPENGL | SDL_FULLSCREEN | SDL_GL_DOUBLEBUFFER);
	else
		Screen = SDL_SetVideoMode(LARGEUR_ECRAN, HAUTEUR_ECRAN, 32, SDL_OPENGL | SDL_GL_DOUBLEBUFFER);  
	
	if(Screen == NULL) return -1;  
	
	SDL_EnableKeyRepeat(10, 10); /* Activation de la répétition des touches */
	SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
	SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 6);
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

	glViewport(0, 0, (GLsizei) Screen->w, (GLsizei) Screen->h);
	glMatrixMode(GL_PROJECTION);
	gluOrtho2D((GLdouble)0, (GLdouble)LARGEUR_ECRAN, (GLdouble)HAUTEUR_ECRAN, (GLdouble)0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity(); 
	glClearColor(1.0,1.0,0.0,1.0);

	return 1;
}

void CMoteur::DessineFPS()
{
    static char txt[100];
	txt[99] = '\0';
    sprintf(txt, "FPS : %d", FPS);
    Fontes[0].DessineTexte(txt, 20, 5, 5);

    nbFPS++;

    if(LastFPS + 1000 < SDL_GetTicks())
    {
        FPS = nbFPS;
        nbFPS = 0;
        LastFPS = SDL_GetTicks();
    }
}

void CMoteur::DebutDessin()
{
	//Appel de cette fonction avant chaque début de dessin ...

	glClear(GL_COLOR_BUFFER_BIT);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void CMoteur::FinDessin()
{
	//STOP: Ici, on dit à la carte graphique de mettre à l'écran les données calculés ...

	if(Fondu)
	{
		if((SDL_GetTicks() >= DebFondu) && (SDL_GetTicks() <= FinFondu))
		{
			int TempsDeb, TempsFin, TempsTot;

			TempsFin = FinFondu - SDL_GetTicks();
			TempsDeb = SDL_GetTicks() - DebFondu;
			TempsTot = TempsFin + TempsDeb;
				
			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity(); 
	
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glEnable(GL_BLEND);
				
			glBegin(GL_QUADS);
				glColor4ub((char)((DebFonduRed * TempsDeb  + FinFonduRed * TempsFin)/ TempsTot), (char)(DebFonduGreen * TempsDeb / TempsTot + FinFonduGreen * TempsFin / TempsTot), (char)(DebFonduBlue * TempsDeb / TempsTot + FinFonduBlue * TempsFin / TempsTot), (char)((DebFonduAlpha * TempsDeb + FinFonduAlpha * TempsFin) / TempsTot));

				glVertex2f(0, HAUTEUR_ECRAN);

				glVertex2f(0, 0 );

				glVertex2f(LARGEUR_ECRAN, 0);

				glVertex2f(LARGEUR_ECRAN, HAUTEUR_ECRAN);
			glEnd();
					
		}
		else if(SDL_GetTicks() > FinFondu)
		{
			Fondu = false;
		}
	}

	glFlush();
	SDL_GL_SwapBuffers();
	        
	if(FPS > 100) SDL_Delay(5);
	else if(FPS > 80) SDL_Delay(4);
	else if(FPS > 60) SDL_Delay(3);
	else SDL_Delay(2);
}

void CMoteur::AfficheTexture(Uint32 nbTex, GLint x, GLint y, GLint w, GLint h, unsigned char red, unsigned char green , unsigned char blue, unsigned char alpha)
{
	//La sa devient intérresant, il faut dessinner une image à l'écran ...

	if(nbTex >= Sprites.size()) return;

	bool inversX = (w < 0);
	if(inversX) w = -w;

	bool inversY = (h < 0);
	if(inversY) h = -h;

	Sprites[nbTex].Affiche(x, y, w, h, red, green, blue, (alpha != 255), alpha, inversX, inversY);

}

int CMoteur::NouvelleFonte(const char *NomFonte)
{
	Fontes.resize(Fontes.size() + 1);
	Fontes[Fontes.size() - 1].LoadFont(NomFonte);
	return Fontes.size() - 1;
}

void CMoteur::DessineTexte(int NbFonte, const char * Texte, int Taille, int x, int y, unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha)
{
	if(NbFonte >= Fontes.size()) return;

	Fontes[NbFonte].DessineTexte(Texte, Taille, x, y, red, green, blue, (alpha != 255), alpha);
}

int CMoteur::NouvelleTexture(const char *CheminImg)
{
	//Chargement d'une texture depuis le disque ...

	Sprites.resize(Sprites.size() + 1);
	Sprites[Sprites.size() - 1].LoadTexture(CheminImg);
	return Sprites.size() - 1;
}


//Fonctions de la classe CTexte ...
CMoteur::CTexte::CTexte()
{

}

CMoteur::CTexte::~CTexte()
{

}

GLint CMoteur::CTexte::ChargerFichier(const char *NomPolice, const char *NomFichier)
{
	char CheminFichier[100];
	sprintf(CheminFichier, "./Images/Fontes/%s/%s", NomPolice, NomFichier);
	return Outils::loadTexture(CheminFichier);
}

void CMoteur::CTexte::LoadFont(const char *NomDeLaPolice)
{	

	surface[0] = ChargerFichier(NomDeLaPolice, "A.png");
	surface[1] = ChargerFichier(NomDeLaPolice, "B.png");
	surface[2] = ChargerFichier(NomDeLaPolice, "C.png");
	surface[3] = ChargerFichier(NomDeLaPolice, "D.png");
	surface[4] = ChargerFichier(NomDeLaPolice, "E.png");
	surface[5] = ChargerFichier(NomDeLaPolice, "F.png");
	surface[6] = ChargerFichier(NomDeLaPolice, "G.png");
	surface[7] = ChargerFichier(NomDeLaPolice, "H.png");
	surface[8] = ChargerFichier(NomDeLaPolice, "I.png");
	surface[9] = ChargerFichier(NomDeLaPolice, "J.png");
	surface[10] = ChargerFichier(NomDeLaPolice, "K.png");
	surface[11] = ChargerFichier(NomDeLaPolice, "L.png");
	surface[12] = ChargerFichier(NomDeLaPolice, "M.png");
	surface[13] = ChargerFichier(NomDeLaPolice, "N.png");
	surface[14] = ChargerFichier(NomDeLaPolice, "O.png");
	surface[15] = ChargerFichier(NomDeLaPolice, "P.png");
	surface[16] = ChargerFichier(NomDeLaPolice, "Q.png");
	surface[17] = ChargerFichier(NomDeLaPolice, "R.png");
	surface[18] = ChargerFichier(NomDeLaPolice, "S.png");
	surface[19] = ChargerFichier(NomDeLaPolice, "T.png");
	surface[20] = ChargerFichier(NomDeLaPolice, "U.png");
	surface[21] = ChargerFichier(NomDeLaPolice, "V.png");
	surface[22] = ChargerFichier(NomDeLaPolice, "W.png");
	surface[23] = ChargerFichier(NomDeLaPolice, "X.png");
	surface[24] = ChargerFichier(NomDeLaPolice, "Y.png");
	surface[25] = ChargerFichier(NomDeLaPolice, "Z.png");
	surface[26] = ChargerFichier(NomDeLaPolice, "DeuxPoints.png");
	surface[27] = ChargerFichier(NomDeLaPolice, "0.png");
	surface[28] = ChargerFichier(NomDeLaPolice, "1.png");
	surface[29] = ChargerFichier(NomDeLaPolice, "2.png");
	surface[30] = ChargerFichier(NomDeLaPolice, "3.png");
	surface[31] = ChargerFichier(NomDeLaPolice, "4.png");
	surface[32] = ChargerFichier(NomDeLaPolice, "5.png");
	surface[33] = ChargerFichier(NomDeLaPolice, "6.png");
	surface[34] = ChargerFichier(NomDeLaPolice, "7.png");
	surface[35] = ChargerFichier(NomDeLaPolice, "8.png");
	surface[36] = ChargerFichier(NomDeLaPolice, "9.png");
	surface[37] = ChargerFichier(NomDeLaPolice, "Espace.png");
	surface[38] = ChargerFichier(NomDeLaPolice, "Apostrophe.png");
	surface[39] = ChargerFichier(NomDeLaPolice, "Point.png");
	surface[40] = ChargerFichier(NomDeLaPolice, "Point exclamation.png");
	surface[41] = ChargerFichier(NomDeLaPolice, "tiret.png");

}

void CMoteur::CTexte::DessineTexte(const char * texte, int taille, int PosX, int PosY, unsigned char red, unsigned char green, unsigned char blue, bool balpha, unsigned char alpha)
{
   	char nbtex;

	glMatrixMode(GL_MODELVIEW);
	
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);

   	glEnable(GL_TEXTURE_2D);

    for(int i = 0; texte[i] != '\0'; i++)
    {
        nbtex = giveLettre(toupper(texte[i]));
        glBindTexture(GL_TEXTURE_2D, surface[nbtex]);

        glBegin(GL_QUADS);
            if(balpha)
				glColor4ub(red, green, blue, alpha);
			else
				glColor3ub(red, green, blue);

            glTexCoord2i (0, 0);
            glVertex2i(PosX + i * taille, PosY + taille);

            glTexCoord2i (0, 1);
            glVertex2i(PosX + i * taille, PosY);

            glTexCoord2i (1, 1);
            glVertex2i(PosX + (i+1) * taille, PosY);

            glTexCoord2i (1, 0);
            glVertex2i(PosX + (i+1) * taille, PosY + taille);
        glEnd();
    }

    glDisable(GL_BLEND);
    glDisable(GL_TEXTURE_2D);

}

int CMoteur::CTexte::giveLettre(char lettre)
{
    if(lettre == 'A') return 0;
    if(lettre == 'B') return 1;
    if(lettre == 'C') return 2;
    if(lettre == 'D') return 3;
    if(lettre == 'E') return 4;
    if(lettre == 'F') return 5;
    if(lettre == 'G') return 6;
    if(lettre == 'H') return 7;
    if(lettre == 'I') return 8;
    if(lettre == 'J') return 9;
    if(lettre == 'K') return 10;
    if(lettre == 'L') return 11;
    if(lettre == 'M') return 12;
    if(lettre == 'N') return 13;
    if(lettre == 'O') return 14;
    if(lettre == 'P') return 15;
    if(lettre == 'Q') return 16;
    if(lettre == 'R') return 17;
    if(lettre == 'S') return 18;
    if(lettre == 'T') return 19;
    if(lettre == 'U') return 20;
    if(lettre == 'V') return 21;
    if(lettre == 'W') return 22;
    if(lettre == 'X') return 23;
    if(lettre == 'Y') return 24;
    if(lettre == 'Z') return 25;
    if(lettre == ':') return 26;
    if(lettre == '0') return 27;
    if(lettre == '1') return 28;
    if(lettre == '2') return 29;
    if(lettre == '3') return 30;
    if(lettre == '4') return 31;
    if(lettre == '5') return 32;
    if(lettre == '6') return 33;
    if(lettre == '7') return 34;
    if(lettre == '8') return 35;
    if(lettre == '9') return 36;
    if(lettre == ' ') return 37;
	if(lettre == '\'') return 38;
	if(lettre == '.') return 39;
	if(lettre == '!') return 40;
	if(lettre == '-') return 41;


    return 37; // Par défaut, on retourne un espace ...
}

//Fonctions pour la classe CSprite ...
CMoteur::CSprite::CSprite()
{
	//Constructeur de CSprite in CMoteur ...
}

CMoteur::CSprite::~CSprite()
{
	//Destructeur
}

void CMoteur::CSprite::Affiche(GLint x, GLint y, GLint w, GLint h, unsigned char red, unsigned char green, unsigned char blue, bool balpha, unsigned char alpha, bool inversX, bool inversY)
{
	//Affichage d'un(e) sprite (image) à l'écran ...

	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, IDTexture);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	float PosTexX, PosTexY;

	glBegin(GL_QUADS);
		if(balpha)
			glColor4ub(red, green, blue, alpha);
		else
			glColor3ub(red, green, blue);

		PosTexX = 0.0;
		PosTexY = 0.0;
		if(inversX) PosTexX = 1.0 - PosTexX;
		if(inversY) PosTexY = 1.0 - PosTexY;
		glTexCoord2f (PosTexX, PosTexY);
		glVertex2f(x, y + h);

		PosTexX = 0.0;
		PosTexY = 1.0;
		if(inversX) PosTexX = 1.0 - PosTexX;
		if(inversY) PosTexY = 1.0 - PosTexY;
		glTexCoord2f (PosTexX, PosTexY);
		glVertex2f(x, y );

		PosTexX = 1.0;
		PosTexY = 1.0;
		if(inversX) PosTexX = 1.0 - PosTexX;
		if(inversY) PosTexY = 1.0 - PosTexY;
		glTexCoord2f (PosTexX, PosTexY);
		glVertex2f(x + w, y);

		PosTexX = 1.0;
		PosTexY = 0.0;
		if(inversX) PosTexX = 1.0 - PosTexX;
		if(inversY) PosTexY = 1.0 - PosTexY;
		glTexCoord2f (PosTexX, PosTexY);
		glVertex2f(x + w, y + h);
	glEnd();

    glDisable(GL_BLEND);
	glDisable(GL_TEXTURE_2D);
	
}

GLint CMoteur::CSprite::LoadTexture(const char * filename)
{
	//Enfin chargerment dele texture depuis un ficher sur le disque ...

	IDTexture = Outils::loadTexture(filename);


}

void CMoteur::ResizeWindow(int larg, int haut)
{
	if(IsFullScreen)
		Screen = SDL_SetVideoMode(0, 0, 32, SDL_OPENGL | SDL_FULLSCREEN | SDL_GL_DOUBLEBUFFER);
	else
		Screen = SDL_SetVideoMode(larg, haut, 32, SDL_OPENGL | SDL_GL_DOUBLEBUFFER); 

	glViewport(0, 0, (GLsizei) Screen->w, (GLsizei) Screen->h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D((GLdouble)0, (GLdouble)LARGEUR_ECRAN, (GLdouble)HAUTEUR_ECRAN, (GLdouble)0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity(); 
}

void CMoteur::RajoutFondu(Uint32 pDebFondu, Uint32 pFinFondu, unsigned char pDebFonduRed, unsigned char pDebFonduGreen, unsigned char pDebFonduBlue, unsigned char pDebFonduAlpha, unsigned char pFinFonduRed, unsigned char pFinFonduGreen, unsigned char pFinFonduBlue, unsigned char pFinFonduAlpha)
{
	DebFondu = pDebFondu;
	FinFondu = pFinFondu;
	
	DebFonduBlue = pDebFonduBlue;
	DebFonduGreen = pDebFonduGreen;
	DebFonduRed = pDebFonduRed;
	DebFonduAlpha = pDebFonduAlpha;

	FinFonduBlue = pFinFonduBlue;
	FinFonduGreen = pFinFonduGreen;
	FinFonduRed = pFinFonduRed;
	FinFonduAlpha = pFinFonduAlpha;

	Fondu = true;
}

//Fin de fichier
