// Author : Chris Tolley
// Date   : 2/21/2004
// texture.h contains a bitmap texture class to use with OpenGL

#include <cstdio>
#include <windows.h>
#include <gl/gl.h>
//#include <gl/glu.h>
//#include <gl/glaux.h>
#include "texture.h"
//#include "Exceptions.h"
#include <vector>
using namespace std;

#include "logger.h"

// Constructor with argument.  filename is the name of the 
// file that holds the bitmap
texture::texture( const char* filename ) : data_(0)
{
	load( filename );
}// End texture::Constructor


// Loads a bitmap into memory from filename
void texture::load( const char* filename )
{
	// Open the file filename
	FILE* f = fopen( filename, "rb" );
	// If f is NULL then something went wrong opening the file and
	// we need to quit.   NOTE:  Need to put exceptions here soon
	if( f == NULL )
	{
//		throw new FileNotFoundException( "texture::load:  Could not open " +
//										string(filename) );
		return;
	}// End if

	// Create a bitmap file header to hold the bitmap file header info
	BITMAPFILEHEADER fileHeader;
	// Read in the header of the bitmap file
	fread( &fileHeader, sizeof( BITMAPFILEHEADER ), 1, f );

	// Verify that the file we have open is a bitmap file
	if( fileHeader.bfType != 0x4D42 )
	{	
//		throw new FileException( "texture::load: Invalid bmp header");
		fclose( f );
		return;
	}// End if

	// Read in the bitmap information header
	fread( &infoHeader_, sizeof( BITMAPINFOHEADER ), 1, f );

	// Move the file pointer to the beginning of the data.
	fseek( f, fileHeader.bfOffBits, SEEK_SET );

	unsigned char temp;

	for( int n = 0; n < infoHeader_.biSizeImage; ++n )
	{
		// Read in one byte of data and store it in data_
		fread( &temp, 1, 1, f );
		data_.push_back( temp );
	}// End for n

	// Make sure that some data was read
	if( data_.size() == 0 )
	{
//		throw new LogicalException( "texture::load:  No data was read in from " +
//									 string( filename ) );
		fclose( f );
		return;
	}// End if

	// Swap the R and B values to get RGB because bitmaps store it BGR
	vector< unsigned char >::iterator it = data_.begin();
	for( it; it != data_.end(); it += 3 )
	{
		unsigned char temp = *it;
		*it = *( it + 2 );
		*( it + 2 ) = temp;
	}// End for it

	// Close the file pointer
	fclose( f );

	// Enable Texturing
	glEnable( GL_TEXTURE_2D );

	// Set up this texture object with OpenGL
	glGenTextures( 1, &id_ );
	glBindTexture(GL_TEXTURE_2D, id_);

		// Set up the filter variables.
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

     // generate the texture image
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, infoHeader_.biWidth,
				 infoHeader_.biHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, data_.begin() );

	// Disable texturing
	glDisable( GL_TEXTURE_2D );
}// End texture::load



// Points r,g,b to the RGB values of the (x,z)th pixel
void texture::getPixel( int x, int z, unsigned char& r, 
								  unsigned char& g, unsigned char& b )
{
	int index = ( z * ( infoHeader_.biWidth * 3 ) ) + ( x * 3 );
	int iHdrWidt = infoHeader_.biWidth;
	iHdrWidt++;
	r = data_[index];
	g = data_[index + 1];
	b = data_[index + 2];
}// End HeightBasedTexture::getPixel



// Points r,g,b to the RGB values of the (x,z)th pixel after wrapping
// x,z around like this texture was tilted
void texture::getTitledPixel( int x, int z, unsigned char& r, 
							 unsigned char& g, unsigned char& b )
{
	// wrap x and z around this texture to calculate the actual position
	// of the pixel values we want

	int X = x % infoHeader_.biHeight;
	int Z = z % infoHeader_.biWidth;

	// Now call getPixel with the new values
	getPixel( X, Z, r, g, b );
}// End texture::getTitledPixel


// Tells OpenGL to use this texture when rendering any
// 2d texture coordinates
void texture::select() const
{
	glBindTexture(GL_TEXTURE_2D, id_);
}// End texture::select


// Tells OpenGL to use the default texture
void texture::deselect() const
{
	glBindTexture( GL_TEXTURE_2D, 0 );
}// End texture::deselect


// Returns the height data
vector< unsigned char >& texture::GetData()
{
	return data_;
}// End texture::GetData


// Destructor
texture::~texture()
{
}// End texture::Destructor