#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>

#include "CTextures.hpp"
#include "CLog.hpp"
extern CLog c_log;



tImage *gl_LoadTGA(const char *nom)
{
	tImage *pImageData = NULL;
	unsigned short width = 0;
	unsigned short height = 0;
	byte length = 0;
	byte imageType = 0;
	byte bits = 0;
	byte orientation = 0; //0 == normal, 20 == top down
	FILE *pFile;
	byte *pImg;
	int channels;
	int counter;
	int colorsRead;
	int i_mem;
	int i = 0;

	pFile = fopen(nom, "rb");
	if(!pFile)
		return NULL;

	pImageData = (tImage*) malloc (sizeof(tImage));

	fread(&length, sizeof(byte), 1, pFile);	       //On remplir l'header TGA
	fseek(pFile, 1, SEEK_CUR);
	fread(&imageType, sizeof(byte), 1, pFile);
	fseek(pFile, 9, SEEK_CUR);
	fread(&width, sizeof(unsigned short), 1, pFile);
	fread(&height, sizeof(unsigned short), 1, pFile);
	fread(&bits, sizeof(byte), 1, pFile);
	fread(&orientation, sizeof(byte), 1, pFile);
	fseek(pFile, length, SEEK_CUR);

	if (orientation == TGA_TOPDOWN)	    // Si topdown tga  || force extraction en topdown
		orientation = 1;
	else if (orientation == TGA_NORMAL)	// Si normal tga
		orientation = 0;

	channels = bits >> 3;

	if(imageType != TGA_RLE)	// no rle compression
	{
		if(channels > 2)	      // 24 or 32 bits tga file
		{
			byte pixels = 0;
			counter = width * height;
			i_mem = width * channels;
			pImageData->data = (byte*) malloc (sizeof(byte) * i_mem * height);
			pImg = pImageData->data;

			if (orientation == 1) // top-down orientation
			{
				unsigned short theight = height;
				pImg += counter * channels;
				while (theight--)
				{
					pImg -= i_mem;
					fread(pImg, i_mem, 1, pFile);
				}
			}
			else //normal orientation
			{
				fread(pImg, i_mem * height, 1, pFile);
			}
			while (counter--) // not very optimized...
			{
				pixels = *pImg;
				*pImg = *(pImg + 2);
				*(pImg + 2) = pixels;
				pImg += channels;
			}
		}
		else if (channels == 2) // 16 bits tga file
		{
			unsigned short pixels = 0;

			channels = 3;
			counter = width * height;
			i_mem = channels * width;
			pImageData->data = ((unsigned char*) malloc (sizeof(unsigned char) * i_mem * height));
			pImg = pImageData->data;

			if (orientation == 1) // top-down orientation
			{
				unsigned short theight = height;
				pImg += counter * channels;
				while (theight--)
				{
					pImg -= i_mem;
					for(int i = 0; i < width; i++)
					{
						fread(&pixels, sizeof(unsigned short), 1, pFile);
						pImg[i * 3] = (unsigned char) (((pixels >> 10) & 0x1f) << 3);
						pImg[i * 3 + 1] = (unsigned char) (((pixels >> 5) & 0x1f) << 3);
						pImg[i * 3 + 2] = (unsigned char) ((pixels & 0x1f) << 3);
					}
				}
			}
			else //normal orientation
			{
				for(int i = 0; i < width*height; i++)
				{
					fread(&pixels, sizeof(unsigned short), 1, pFile);
					pImg[i * 3] = (unsigned char) (((pixels >> 10) & 0x1f) << 3);
					pImg[i * 3 + 1] = (unsigned char) (((pixels >> 5) & 0x1f) << 3);
					pImg[i * 3 + 2] = (unsigned char) ((pixels & 0x1f) << 3);
				}
			}
		}
		else if (channels == 1) // 8 bits black & white tga file
		{
			byte pixels = 0;
			channels = 3;
			counter = width * height;
			i_mem = channels * width;
			pImageData->data = ((unsigned char*)malloc(sizeof(unsigned char) * i_mem * height));
			pImg = pImageData->data;

			if (orientation == 1) // top-down orientation
			{
				unsigned short theight = height;
				pImg += counter * channels;
				while (theight--)
				{
					pImg -= width * channels;
					for(int i = 0; i < width; i++)
					{
						fread(&pixels, sizeof(byte), 1, pFile);
						pImg[i * 3] = pixels;
						pImg[i * 3 + 1] = pixels;
						pImg[i * 3 + 2] = pixels;
					}
				}
			}
			else //normal orientation
			{
				for(int i = 0; i < counter; i++)
				{
					fread(&pixels, sizeof(byte), 1, pFile);
					pImg[i * 3] = pixels;
					pImg[i * 3 + 1] = pixels;
					pImg[i * 3 + 2] = pixels;
				}
			}
		}
	    else
			return NULL;
	}
	else
	{
		int rleID = 0;
		colorsRead = 0;
		counter = width * height;
		i_mem = counter * channels;
		pImageData->data = (byte*) malloc(sizeof(byte) * i_mem);
		byte *pColors = (byte*) malloc(sizeof(byte) * channels);
		pImg = pImageData->data;


		while(i < counter)
		{
			fread(&rleID, sizeof(byte), 1, pFile);
      		if(rleID < 128)
			{
				rleID++;
				while(rleID)
				{
					fread(pColors, sizeof(byte) * channels, 1, pFile);
					pImageData->data[colorsRead + 0] = pColors[2];
					pImageData->data[colorsRead + 1] = pColors[1];
					pImageData->data[colorsRead + 2] = pColors[0];

					if(channels == 4)
						pImageData->data[colorsRead + 3] = pColors[3];
					i++;
					rleID--;
					colorsRead += channels;
				}
			}
			else
			{
				rleID -= 127;
				fread(pColors, sizeof(byte) * channels, 1, pFile);
				while(rleID)
				{
					pImageData->data[colorsRead + 0] = pColors[2];
					pImageData->data[colorsRead + 1] = pColors[1];
					pImageData->data[colorsRead + 2] = pColors[0];

					if(channels == 4)
						pImageData->data[colorsRead + 3] = pColors[3];
					i++;
					rleID--;
					colorsRead += channels;
				}
			}
        }
	}

	fclose(pFile);

	pImageData->channels = channels;
	pImageData->sizeX 	 = width;
	pImageData->sizeY 	 = height;
	return pImageData;
}


void gl_ShotBMP(const char *name, int sizeX, int sizeY)
{
  char nom[50] = "";
  sprintf (nom,"%s.bmp",name);

  FILE *fp;
  if ((fp = fopen(nom,"rb"))) {
      // alors il existe deja, on reécrit un autre nom
      sprintf (nom,"%s_%4.4i.bmp",name,(int)++val_shot);
      fclose(fp);
  }
	BITMAPFILEHEADER fileheader;
	BITMAPINFOHEADER infoheader;
    // on se place dans le mode MATRIX (soit pixelisé)
    int Stride = /*(sizeX * 3 + 3) & ~0x3*/ (sizeX * 3);
    int Size = sizeY * Stride;
    unsigned char* pixels = new unsigned char[Size];
    memset      (pixels,0,Size);
    glReadPixels(0, 0, sizeX, sizeY,GL_RGB, GL_UNSIGNED_BYTE,pixels);
    c_log.printf("  (gl_ShotBMP) : Size=%i sur [ (0,0);(%i,%i) ] et 3 plans (RGB) taille ligne=%i",Size,sizeX,sizeY,Stride);

    FILE *sub;

    // Définition de tous les champs des en-tête
    // En-tête de fichier BITMAPFILEHEADER
    fileheader.bfSize = sizeof(BITMAPFILEHEADER);
    fileheader.bfType = 0x4D42;
    fileheader.bfReserved1 = 0;
    fileheader.bfReserved2 = 0;
    fileheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    // En-tête d'informations
    infoheader.biSize = sizeof(BITMAPINFOHEADER);
    infoheader.biPlanes = 1;
    infoheader.biBitCount = 24;
    infoheader.biCompression = 0;
    infoheader.biSizeImage = Size;
    infoheader.biXPelsPerMeter = 0;
    infoheader.biYPelsPerMeter = 0;
    infoheader.biClrUsed = 0;
    infoheader.biClrImportant = 0;
    infoheader.biWidth = sizeX;
    infoheader.biHeight = sizeY;

    if((sub = fopen(nom,"wb"))==NULL) {
        c_log << "Erreur lors de creation du fchier 'sub' dans gl_ShotBMP(...)";
        return;
    }
	fwrite( &fileheader, sizeof(BITMAPFILEHEADER), 1, sub);
	fwrite( &infoheader, sizeof(BITMAPINFOHEADER), 1, sub);

    unsigned char tempRGB;
    for (unsigned int i = 0; i < infoheader.biSizeImage; i += 3)
    {
        tempRGB = pixels[i];
        pixels[i] = pixels [i + 2];
        pixels[i + 2] = tempRGB;
    }

	fwrite( pixels, Size, 1, sub);
	delete [] pixels;

	fclose(sub);
}


bool gl_GenTexture(unsigned int &texture, const char *strFileName, GLfloat min_filter, GLfloat mag_filter, GLfloat wrap_S, GLfloat wrap_T, bool mipmap)
{
	int textureType;
	tImage *pImage;

	if(!strFileName)
		return false;

	pImage = gl_LoadTGA(strFileName);

	if(pImage == NULL)
		return false;

  glGenTextures(1, &texture);
	glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
	glBindTexture(GL_TEXTURE_2D, texture);

	textureType = GL_RGB;

	if(pImage->channels == 4)
		textureType = GL_RGBA;

  if (pImage->channels < 3)
		return false;

	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_S);
	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_T);
	glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	if (mipmap)
		gluBuild2DMipmaps(GL_TEXTURE_2D, pImage->channels, pImage->sizeX, pImage->sizeY, textureType, GL_UNSIGNED_BYTE, pImage->data);
	else
		glTexImage2D(GL_TEXTURE_2D, 0, pImage->channels, pImage->sizeX, pImage->sizeY, 0, textureType, GL_UNSIGNED_BYTE, pImage->data);

	if (pImage)
	{
		if (pImage->data)
		{
			free(pImage->data);
		}
		free(pImage);
	}
	return true;
}



bool gl_LoadAllTextures(const char **textures, unsigned int *id_tex, const short nb_tex)
{
    c_log << "(gl_LoadAllTextures) - début";
    for (short i=0;i<nb_tex;i++)
    {
        bool etat = false;
        etat = gl_GenTexture(id_tex[i],textures[i],GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, 1);

        c_log.printf("gl_GenTexture(%s, %i) =)>  %s",textures[i],id_tex[i],(etat?"<b>true</b>":"<b>fase</b>"));

        if (!etat) {
           c_log << "(gl_LoadAllTextures) - fin";
           return false;
        }
    }
    c_log << "(gl_LoadAllTextures) - fin";
    return true;
}


