/*
*	texture2D.cpp
*
*	Programmé par Hillaire Sébastien
*
*
*	Se référé au fichier en-tête .h pour plus de détail
*
*/


#include "texture2D.h"
#include "MemoryManager/DebugNew.h"

using namespace RenderSystem;

Texture2D::Texture2D()
{
	id=0;
	target = GL_TEXTURE_2D;
}

Texture2D::~Texture2D()
{
	//supprime l'objet de texture OpenGL en mémoire vidéo
	glDeleteTextures(1,&id);
	id=0;
}

bool Texture2D::loadBMP(char *filePath, bool useMipMap, int magFilter, int minFilter, int sWrap, int tWrap, unsigned int levelOfDetail, bool generateAlpha, unsigned char rBlended, unsigned char gBlended, unsigned char bBlended, unsigned char alphaBlendValue, unsigned char alphaDefault,int anisotropy)
{
	bool ret=false;

	//efface la texture si elle existe déja
	if(id!=0)
	{
		glDeleteTextures(1,&id);
		id=0;
		//std::cout<<"realloc BMP";
	}

	////////////////////////////CHARGEMENT DE LA TEXTURE APROPRIE/////////////////////////

	//On choisit la bonne implémentation de chargement suivant l'extension du fichier
	Image *img = NULL;

	//pointage sur l'extension(l'adresse du premier caractère de l'extension)
	char *pc = filePath + strlen(filePath)-3;

	if(strcmp(pc,"bmp")==0)//!!! attention bmp en minuscule !!!
	{
		img = new ImageBMP();
	}
	else if(strcmp(pc,"tga")==0)//!!! attention bmp en minuscule !!!
	{
		img = new ImageTGA();
	}
	else
	{
		delete img;
		return false;
	}



	if(img->load(filePath))
	{
		//genere l'objet de texture OpenGL en mémoire vidéo
		glGenTextures(1,&id);

		if(levelOfDetail<0)
		{//vérification validité de detailDown(pour baisser niveau de détail de la texture
			levelOfDetail=0;
		}
		int detailSizeFactor=(int)pow((float)2,(float)levelOfDetail);					//décalage a effectuer entre les pixel pour baisser les détails
		int size=(img->getWidth()*img->getHeight()*4)/detailSizeFactor;	//taille du buffer image avec baisse de détail prise en compte
		if(size<4)
		{//vérification validité de la taille minimum du buffer image ( 4 -> r,g,b,a )
			size=4;
		}
		unsigned char *temp=new unsigned char[ size ];	//création buffer de texture avec les composantes alpha
		unsigned char * pc=img->getDatasPointer();		//initialisation pointeur temporaire sur le buffer image
		unsigned char r,v,b;
		bool NeedBlend=false;							//permet de vérifier si on doit utiliser la transparence
		int tailleDestX=img->getWidth()/detailSizeFactor;
		int tailleDestY=img->getHeight()/detailSizeFactor;
		if(tailleDestX<1)
		{//vérification de la bornes x de la texture destination
			tailleDestX=1;
		}
		if(tailleDestY<1)
		{//vérification de la bornes y de la texture destination
			tailleDestY=1;
		}
		
		//la texture source contient 3 composant (rgb)
		//la texture destination contient 4 composant (rgba)

		for(int i=0;i<tailleDestX;i++)
		{
			for(int j=0;j<tailleDestY;j++)
			{
				int RX=(int)((float)img->getWidth()*((float)i/(float)tailleDestX));//coordonnée x du pixel a choisir dans la texture source
				int RY=(int)((float)img->getHeight()*((float)j/(float)tailleDestY));//coordonnée y du pixel a choisir dans la texture source

				//extraction et affectation de la composante r
				r=pc[RX*3+RY*3*img->getWidth()];
				temp[i*4+j*4*tailleDestX]=r;

				//extraction et affectation de la composante g
				v=pc[RX*3+RY*3*img->getWidth()+1];
				temp[i*4+j*4*tailleDestX+1]=v;

				//extraction et affectation de la composante b
				b=pc[RX*3+RY*3*img->getWidth()+2];
				temp[i*4+j*4*tailleDestX+2]=b;

				if( r==rBlended && v==gBlended && b==bBlended )
				{//la couleur correspond a la couleur de blend désirer
					temp[i*4+j*4*tailleDestX+3]=alphaBlendValue;				//on assigne le alpha de transparence
					NeedBlend=true;
				}
				else
				{
					temp[i*4+j*4*tailleDestX+3]=alphaDefault;
				}
			}
		}

		//initialisation de l'objet de texture
		glBindTexture(GL_TEXTURE_2D,id);
		
		//option de répetition
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,sWrap);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,tWrap);

		//filtrage a appliquer en cas d'étirement et de ressérage de la texture
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,magFilter);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,minFilter);

		setCurrentAnisotropy(anisotropy);

		if(useMipMap)
		{//on doit utiliser les mipmap
			if( NeedBlend && generateAlpha)
			{//on doit génerer une composante alpha dans la texture
				gluBuild2DMipmaps(GL_TEXTURE_2D,GL_RGBA,tailleDestX,tailleDestY,GL_RGBA,GL_UNSIGNED_BYTE,temp);
			}
			else
			{//on ne doit pas génerer une composante alpha dans la texture
				gluBuild2DMipmaps(GL_TEXTURE_2D,GL_RGB,tailleDestX,tailleDestY,GL_RGBA,GL_UNSIGNED_BYTE,temp);
			}
		}
		else
		{//on ne doit pas utiliser les mipmap
			if( NeedBlend && generateAlpha)
			{//on doit génerer une composante alpha dans la texture
				glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,tailleDestX,tailleDestY,0,GL_RGBA,GL_UNSIGNED_BYTE,temp);
			}
			else
			{//on ne doit pas génerer une composante alpha dans la texture
				glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,tailleDestX,tailleDestY,0,GL_RGBA,GL_UNSIGNED_BYTE,temp);
			}
		}

		ret = true;

		delete [] temp;
		delete img;
	}

	return ret;
}


bool Texture2D::loadLocalHeight(char *filePath1,char *filePath2, bool useMipMap, int magFilter, int minFilter, int sWrap, int tWrap, unsigned int levelOfDetail, int anisotropy)
{
	bool ret=false;

	//efface la texture si elle existe déja
	if(id!=0)
	{
		glDeleteTextures(1,&id);
		id=0;
		//std::cout<<"realloc LocalHeight";
	}

	////////////////////////////CHARGEMENT DE LA TEXTURE APROPRIE/////////////////////////

	//On choisit la bonne implémentation de chargement suivant l'extension du fichier
	Image *imgRGB = NULL;
	Image *imgA = NULL;

	//pointage sur l'extension(l'adresse du premier caractère de l'extension)
	char *pc = filePath1 + strlen(filePath1)-3;
	if(strcmp(pc,"bmp")==0)//!!! attention bmp en minuscule !!!
	{
		imgRGB = new ImageBMP();
	}
	else if(strcmp(pc,"tga")==0)//!!! attention bmp en minuscule !!!
	{
		imgRGB = new ImageTGA();
	}
	else
	{
		delete imgRGB;
		return false;
	}
	pc = filePath2 + strlen(filePath2)-3;
	if(strcmp(pc,"bmp")==0)//!!! attention bmp en minuscule !!!
	{
		imgA = new ImageBMP();
	}
	else if(strcmp(pc,"tga")==0)//!!! attention bmp en minuscule !!!
	{
		imgA = new ImageTGA();
	}
	else
	{
		delete imgA;
		return false;
	}


	if(imgRGB->load(filePath1) && imgA->load(filePath2))
	{
		//les deux images doivent avoir même taille!
		if( (imgRGB->getHeight()!=imgA->getHeight()) || (imgRGB->getWidth()!=imgA->getWidth()) )
		{
			delete imgRGB;
			delete imgA;
			return false;
		}

		//genere l'objet de texture OpenGL en mémoire vidéo
		glGenTextures(1,&id);

		if(levelOfDetail<0)
		{//vérification validité de detailDown(pour baisser niveau de détail de la texture
			levelOfDetail=0;
		}
		int detailSizeFactor=(int)pow((float)2,(float)levelOfDetail);					//décalage a effectuer entre les pixel pour baisser les détails
		int size=(imgRGB->getWidth()*imgRGB->getHeight()*4)/detailSizeFactor;	//taille du buffer image avec baisse de détail prise en compte
		if(size<4)
		{//vérification validité de la taille minimum du buffer image ( 4 -> r,g,b,a )
			size=4;
		}
		unsigned char *temp=new unsigned char[ size ];	//création buffer de texture avec les composantes alpha
		unsigned char * pc=imgRGB->getDatasPointer();		//initialisation pointeur temporaire sur le buffer image
		unsigned char * pca=imgA->getDatasPointer();		//initialisation pointeur temporaire sur le buffer image
		int tailleDestX=imgRGB->getWidth()/detailSizeFactor;
		int tailleDestY=imgRGB->getHeight()/detailSizeFactor;
		if(tailleDestX<1)
		{//vérification de la bornes x de la texture destination
			tailleDestX=1;
		}
		if(tailleDestY<1)
		{//vérification de la bornes y de la texture destination
			tailleDestY=1;
		}
		
		//la texture source contient 3 composant (rgb)
		//la texture destination contient 4 composant (rgba)

		for(int i=0;i<tailleDestX;i++)
		{
			for(int j=0;j<tailleDestY;j++)
			{
				int RX=(int)((float)imgRGB->getWidth()*((float)i/(float)tailleDestX));//coordonnée x du pixel a choisir dans la texture source
				int RY=(int)((float)imgRGB->getHeight()*((float)j/(float)tailleDestY));//coordonnée y du pixel a choisir dans la texture source

				//extraction et affectation de la composante r
				temp[i*4+j*4*tailleDestX]=pc[RX*3+RY*3*imgRGB->getWidth()+0];
				//extraction et affectation de la composante g
				temp[i*4+j*4*tailleDestX+1]=pc[RX*3+RY*3*imgRGB->getWidth()+1];
				//extraction et affectation de la composante b
				temp[i*4+j*4*tailleDestX+2]=pc[RX*3+RY*3*imgRGB->getWidth()+2];
				//on assigne le alpha
				temp[i*4+j*4*tailleDestX+3]=pca[RX*3+RY*3*imgA->getWidth()+0];
			}
		}

		//initialisation de l'objet de texture
		glBindTexture(GL_TEXTURE_2D,id);
		
		//option de répetition
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,sWrap);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,tWrap);

		//filtrage a appliquer en cas d'étirement et de ressérage de la texture
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,magFilter);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,minFilter);

		setCurrentAnisotropy(anisotropy);

		if(useMipMap)
		{//on doit utiliser les mipmap
			gluBuild2DMipmaps(GL_TEXTURE_2D,GL_RGBA,tailleDestX,tailleDestY,GL_RGBA,GL_UNSIGNED_BYTE,temp);
		}
		else
		{//on ne doit pas utiliser les mipmap
			glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,tailleDestX,tailleDestY,0,GL_RGBA,GL_UNSIGNED_BYTE,temp);
		}

		ret = true;

		delete [] temp;
		delete imgRGB;
		delete imgA;
	}

	return ret;
}

