#include "texture.h"

// LoadBitmapFile
// desc: Returns a pointer to the bitmap image of the bitmap specified
//       by filename. Also returns the bitmap header information.
//		 No support for 8-bit bitmaps.
unsigned char *CTexture::LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader)
{
	FILE *filePtr;							// the file pointer
	BITMAPFILEHEADER	bitmapFileHeader;		// bitmap file header
	unsigned char		*bitmapImage;			// bitmap image data
	int				imageIdx = 0;			// image index counter
	unsigned char		tempRGB;				// swap variable

	// open filename in "read binary" mode
	//filePtr = fopen(filename, "rb");
	fopen_s(&filePtr, filename, "rb");
	if (filePtr == NULL)
		return NULL;

	// read the bitmap file header
	fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);
	
	// verify that this is a bitmap by checking for the universal bitmap id
	if (bitmapFileHeader.bfType != BITMAP_ID)
	{
		fclose(filePtr);
		return NULL;
	}

	// read the bitmap information header
	fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);

	// move file pointer to beginning of bitmap data
	fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);

	// allocate enough memory for the bitmap image data
	bitmapImage = (unsigned char*)malloc(bitmapInfoHeader->biSizeImage);

	// verify memory allocation
	if (!bitmapImage)
	{
		free(bitmapImage);
		fclose(filePtr);
		return NULL;
	}

	// read in the bitmap image data
	fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr);

	// make sure bitmap image data was read
	if (bitmapImage == NULL)
	{
		fclose(filePtr);
		return NULL;
	}

	// swap the R and B values to get RGB since the bitmap color format is in BGR
	for (imageIdx = 0; imageIdx < (int)bitmapInfoHeader->biSizeImage; imageIdx+=3)
	{
		tempRGB = bitmapImage[imageIdx];
		bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
		bitmapImage[imageIdx + 2] = tempRGB;
	}

	// close the file and return the bitmap image data
	fclose(filePtr);
	return bitmapImage;
}

/*****************************************************************************
 LoadBitmapFileWithAlpha

 Loads a bitmap file normally, and then adds an alpha component to use for
 blending
*****************************************************************************/
unsigned char *CTexture::LoadBitmapFileWithAlpha(char *filename, BITMAPINFOHEADER *bitmapInfoHeader)
{
  unsigned char *bitmapImage = LoadBitmapFile(filename, bitmapInfoHeader);
  unsigned char *bitmapWithAlpha = (unsigned char *)malloc(bitmapInfoHeader->biSizeImage * 4 / 3);

  // loop through the bitmap data
  for (int src = 0, dst = 0; src < (int)bitmapInfoHeader->biSizeImage; src +=3, dst +=4)
  {
    // if the pixel is black, set the alpha to 0. Otherwise, set it to 255.
    if (bitmapImage[src] == 0 && bitmapImage[src+1] == 0 && bitmapImage[src+2] == 0)
      bitmapWithAlpha[dst+3] = 0;
    else
      bitmapWithAlpha[dst+3] = 0xFF;

    // copy pixel data over
    bitmapWithAlpha[dst] = bitmapImage[src];
    bitmapWithAlpha[dst+1] = bitmapImage[src+1];
    bitmapWithAlpha[dst+2] = bitmapImage[src+2];
  }

  free(bitmapImage);

  return bitmapWithAlpha;
} // end LoadBitmapFileWithAlpha()

// LoadPCXFile()
// desc: loads a PCX file into memory
unsigned char *CTexture::LoadPCXFile(char *filename, PCXHEADER *pcxHeader)
{
     int idx = 0;                  // counter index
     int c;                             // used to retrieve a char from the file
     int i;                             // counter index
     int numRepeat;      
     FILE *filePtr;                // file handle
     int width;                         // pcx width
     int height;                        // pcx height
     unsigned char *pixelData;     // pcx image data
     unsigned char *paletteData;   // pcx palette data

     // open PCX file
     //filePtr = fopen(filename, "rb");
	 fopen_s(&filePtr, filename, "rb");
     if (filePtr == NULL)
          return NULL;

     // retrieve first character; should be equal to 10
     c = getc(filePtr);
     if (c != 10)
     {
          fclose(filePtr);
          return NULL;
     }

     // retrieve next character; should be equal to 5
     c = getc(filePtr);
     if (c != 5)
     {
          fclose(filePtr);
          return NULL;
     }

     // reposition file pointer to beginning of file
     rewind(filePtr);

     // read 4 characters of data to skip
     fgetc(filePtr);
     fgetc(filePtr);
     fgetc(filePtr);
     fgetc(filePtr);

     // retrieve leftmost x value of PCX
     pcxHeader->xMin = fgetc(filePtr);       // loword
     pcxHeader->xMin |= fgetc(filePtr) << 8; // hiword

     // retrieve bottom-most y value of PCX
     pcxHeader->yMin = fgetc(filePtr);       // loword
     pcxHeader->yMin |= fgetc(filePtr) << 8; // hiword

     // retrieve rightmost x value of PCX
     pcxHeader->xMax = fgetc(filePtr);       // loword
     pcxHeader->xMax |= fgetc(filePtr) << 8; // hiword

     // retrieve topmost y value of PCX
     pcxHeader->yMax = fgetc(filePtr);       // loword
     pcxHeader->yMax |= fgetc(filePtr) << 8; // hiword

     // calculate the width and height of the PCX
     width = pcxHeader->xMax - pcxHeader->xMin + 1;
     height = pcxHeader->yMax - pcxHeader->yMin + 1;

     // allocate memory for PCX image data
     pixelData = (unsigned char*)malloc(width*height);

     // set file pointer to 128th byte of file, where the PCX image data starts
     fseek(filePtr, 128, SEEK_SET);
     
     // decode the pixel data and store
     while (idx < (width*height))
     {
          c = getc(filePtr);
          if (c > 0xbf)
          {
               numRepeat = 0x3f & c;
               c = getc(filePtr);

               for (i = 0; i < numRepeat; i++)
               {
                    pixelData[idx++] = c;
               }
          }
          else
               pixelData[idx++] = c;

          fflush(stdout);
     }

     // allocate memory for the PCX image palette
     paletteData = (unsigned char*)malloc(768);

     // palette is the last 769 bytes of the PCX file
     fseek(filePtr, -769, SEEK_END);

     // verify palette; first character should be 12
     c = getc(filePtr);
     if (c != 12)
     {
          fclose(filePtr);
          return NULL;
     }

     // read and store all of palette
     for (i = 0; i < 768; i++)
     {
          c = getc(filePtr);
          paletteData[i] = c;
     }

     // close file and store palette in header
     fclose(filePtr);
     pcxHeader->palette = paletteData;

     // return the pixel image data
     return pixelData;
}

// LoadPCXTexture()
// desc: loads a PCX image file as a texture
void CTexture::LoadPCXTexture(char *filename)
{
     PCXHEADER texInfo;            // header of texture
//     texture_t *thisTexture;       // the texture
     unsigned char *unscaledData;// used to calculate pcx
     int i;                             // index counter
     int j;                             // index counter
     int w;                         // width of texture
     int h;                        // height of texture

     // load the PCX file into the texture struct
     data = LoadPCXFile(filename, &texInfo);
     if (data == NULL)
     {
          free(data);
     //     return NULL;
     }

     // store the texture information
     palette = texInfo.palette;
     width = texInfo.xMax - texInfo.xMin + 1;
     height = texInfo.yMax - texInfo.yMin + 1;
     textureType = PCX;

	w = width;
     h = height;

     // allocate memory for the unscaled data
     unscaledData = (unsigned char*)malloc(w*h*4);

     // store the unscaled data via the palette
     for (j = 0; j < h; j++) 
     {
          for (i = 0; i < w; i++) 
          {
               unscaledData[4*(j*w+i)+0] = (unsigned char)palette[3*data[j*w+i]+0];
               unscaledData[4*(j*w+i)+1] = (unsigned char)palette[3*data[j*w+i]+1];
               unscaledData[4*(j*w+i)+2] = (unsigned char)palette[3*data[j*w+i]+2];
               unscaledData[4*(j*w+i)+3] = (unsigned char)255;
          }
     }

     // find width and height's nearest greater power of 2

     // find width's
     i = 0;
     while (w)
     {
          w /= 2;
          i++;
     }
     scaledHeight = (long)pow(2.0, i-1);

     // find height's
     i = 0;
     while (h)
     {
          h /= 2;
          i++;
     }
     scaledWidth = (long)pow(2.0, i-1);

     // clear the texture data
     if (data != NULL)
     {
          free(data);
          data = NULL;
     }

     // reallocate memory for the texture data
     data = (unsigned char*)malloc(scaledWidth*scaledHeight*4);
     
     // use the GL utility library to scale the texture to the unscaled dimensions
     gluScaleImage(GL_RGBA, this->width, this->height, GL_UNSIGNED_BYTE, unscaledData, scaledWidth, scaledHeight, GL_UNSIGNED_BYTE, data);

	free(unscaledData);
//     return thisTexture;

}

// LoadBMPTexture()
// desc: loads a texture of the BMP format
void CTexture::LoadBMPTexture(char *filename)
{
	BITMAPINFOHEADER texInfo;		// BMP header

	// store BMP data in texture
	data = LoadBitmapFileWithAlpha(filename, &texInfo);
	if (data == NULL)
	{
		free(data);
	}
	
	// store texture information
	width = texInfo.biWidth;
	height = texInfo.biHeight;
	palette = NULL;
	scaledHeight = 0;
	scaledWidth = 0;
	textureType = BMP;
}

// LoadTexture()
// desc: loads a texture given the filename
void CTexture::LoadTexture(char *filename)
{
	char *extStr;

	// get extension from filename
	extStr = strchr(filename, '.');
	extStr++;

	// set the texture type based on extension of filename
	if ((_strcmpi(extStr, "BMP") == 0) || (_strcmpi(extStr, "bmp") == 0))
		LoadBMPTexture(filename);
	else if ((_strcmpi(extStr, "PCX") == 0) || (_strcmpi(extStr, "pcx") == 0) )
		LoadPCXTexture(filename);
	
	else if ((_strcmpi(extStr, "TGA") == 0) || (_strcmpi(extStr, "tga") == 0) )
		LoadTGATexture(filename);
}

// LoadTGAFile()
// desc: loads a TGA file defined by filename
unsigned char *CTexture::LoadTGAFile(char *filename, TGAHEADER *tgaHeader)
{
	FILE *filePtr;
	unsigned char ucharBad;	// garbage data
	short int	sintBad;		// garbage data
	long	imageSize;		// size of TGA image
	int colorMode;			// 4 for RGBA, 3 for RGB
	long imageIdx;			// counter variable
	unsigned char colorSwap;	// swap variable
	unsigned char *imageData;	// the TGA data

	// open the TGA file
	//filePtr = fopen(filename, "rb");
	fopen_s(&filePtr, filename, "rb");
	if (!filePtr)
		return NULL;
	
	// read first two bytes of garbage
	fread(&ucharBad, sizeof(unsigned char), 1, filePtr);
	fread(&ucharBad, sizeof(unsigned char), 1, filePtr);

	// read in the image type
	fread(&tgaHeader->imageTypeCode, sizeof(unsigned char), 1, filePtr);

	// for our purposes, the image type should be either a 2 or a 3
	if ((tgaHeader->imageTypeCode != 2) && (tgaHeader->imageTypeCode != 3))
	{
		fclose(filePtr);
		return NULL;
	}

	// read 13 bytes of garbage data
	fread(&sintBad, sizeof(short int), 1, filePtr);
	fread(&sintBad, sizeof(short int), 1, filePtr);
	fread(&ucharBad, sizeof(unsigned char), 1, filePtr);
	fread(&sintBad, sizeof(short int), 1, filePtr);
	fread(&sintBad, sizeof(short int), 1, filePtr);

	// read image dimensions
	fread(&tgaHeader->imageWidth, sizeof(short int), 1, filePtr);
	fread(&tgaHeader->imageHeight, sizeof(short int), 1, filePtr);

	// read bit depth
	fread(&tgaHeader->bitCount, sizeof(unsigned char), 1, filePtr);

	// read garbage
	fread(&ucharBad, sizeof(unsigned char), 1, filePtr);

	// colormode -> 3 = BGR, 4 = BGRA
	colorMode = tgaHeader->bitCount / 8;
	imageSize = tgaHeader->imageWidth * tgaHeader->imageHeight * colorMode;

	// allocate memory for image data
	imageData = (unsigned char*)malloc(sizeof(unsigned char)*imageSize);

	// read image data
	fread(imageData, sizeof(unsigned char), imageSize, filePtr);

	// change BGR to RGB so OpenGL can use the data
	for (imageIdx = 0; imageIdx < imageSize; imageIdx += colorMode)
	{
		colorSwap = imageData[imageIdx];
		imageData[imageIdx] = imageData[imageIdx+2];
		imageData[imageIdx + 2] = colorSwap;
	}

	// close the file
	fclose(filePtr);

	return imageData;
}

// LoadTGATexture()
// desc: loads a TGA as a texture
void CTexture::LoadTGATexture(char *filename)
{
	TGAHEADER tga;		// BMP header

	// store BMP data in texture
	data = LoadTGAFile(filename, &tga);
	if (data == NULL)
	{
		free(data);
	}
	
	// store texture information
	width = tga.imageWidth;
	height = tga.imageHeight;
	palette = NULL;
	scaledHeight = 0;
	scaledWidth = 0;
	tgaImageCode = tga.imageTypeCode;
	bitDepth = tga.bitCount;
	textureType = TGA;
}