#include "gltools.h"
#include "../loaders/stb_image.h"

// Define targa header. This is only used locally.
#pragma pack(1)
typedef struct
{
	GLbyte	identsize;              // Size of ID field that follows header (0)
	GLbyte	colorMapType;           // 0 = None, 1 = paletted
	GLbyte	imageType;              // 0 = none, 1 = indexed, 2 = rgb, 3 = grey, +8=rle
	unsigned short	colorMapStart;          // First colour map entry
	unsigned short	colorMapLength;         // Number of colors
	unsigned char 	colorMapBits;   // bits per palette entry
	unsigned short	xstart;                 // image x origin
	unsigned short	ystart;                 // image y origin
	unsigned short	width;                  // width in pixels
	unsigned short	height;                 // height in pixels
	GLbyte	bits;                   // bits per pixel (8 16, 24, 32)
	GLbyte	descriptor;             // image descriptor
} TGAHEADER;
#pragma pack(8)


GLbyte *gltLoadRGF(int resourceId, GLint *iWidth, GLint *iHeight, GLint *iComponents, GLenum *eFormat)
{
	HRSRC		rec;
	HGLOBAL		handle;
	int bpp, format;
	long lImageSize;
	GLbyte	*pBits = NULL;          // Pointer to bits

	LPCSTR lpResourceId = MAKEINTRESOURCE(resourceId);

	rec = FindResource(NULL, lpResourceId, "RGF");
	handle = LoadResource(NULL, rec);

	// Default/Failed values
	*iWidth = 0;
	*iHeight = 0;
	*eFormat = GL_BGRA_EXT;
	*iComponents = GL_RGBA8;

	MEMFILE *memfile;
	memfile = (MEMFILE *)msys_mallocAlloc(sizeof(MEMFILE));

	memfile->data = LockResource(handle);
	memfile->length = SizeofResource(NULL, rec);
	memfile->pos = 0;

	memread(&format, 4, (unsigned int)memfile);
	memread(&bpp, 4, (unsigned int)memfile);
	memread(iWidth, 4, (unsigned int)memfile);
	memread(iHeight, 4, (unsigned int)memfile);

	lImageSize = (*iWidth) * (*iHeight) * bpp;

	// Allocate memory and check for success
	pBits = (GLbyte*)msys_mallocAlloc(lImageSize);
	if(pBits == NULL)
		return NULL;

	// Read in the bits
	// Check for read error. This should catch RLE or other 
	// weird formats that I don't want to recognize
	if(memread(pBits, lImageSize, (unsigned int)memfile) ==0)
	{
		msys_mallocFree(pBits);
		return NULL;
	}

	// Done with File
	memclose((unsigned int)memfile);

	// Return pointer to image data
	return pBits;
}

////////////////////////////////////////////////////////////////////
// Allocate memory and load targa bits. Returns pointer to new buffer,
// height, and width of texture, and the OpenGL format of data.
// Call free() on buffer when finished!
// This only works on pretty vanilla targas... 8, 24, or 32 bit color
// only, no palettes, no RLE encoding.
GLbyte *gltLoadJPG(int resourceId, GLint *iWidth, GLint *iHeight, GLint *iComponents, GLenum *eFormat)
{
	HRSRC		rec;
	HGLOBAL		handle;

	LPCSTR lpResourceId = MAKEINTRESOURCE(resourceId);

	rec = FindResource(NULL, lpResourceId, "JPG");
	handle = LoadResource(NULL, rec);

	MEMFILE *memfile;
	memfile = (MEMFILE *)msys_mallocAlloc(sizeof(MEMFILE));

	memfile->data = LockResource(handle);
	memfile->length = SizeofResource(NULL, rec);
	memfile->pos = 0;

	//stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)';
	int req_comp = 4;
	GLbyte* pixels = (GLbyte*)stbi_load_from_memory((unsigned char*)memfile->data, memfile->length, iWidth, iHeight, iComponents, req_comp);

	*eFormat = GL_RGBA;
	*iComponents = GL_RGBA8;

	return pixels;
}

////////////////////////////////////////////////////////////////////
// Allocate memory and load targa bits. Returns pointer to new buffer,
// height, and width of texture, and the OpenGL format of data.
// Call free() on buffer when finished!
// This only works on pretty vanilla targas... 8, 24, or 32 bit color
// only, no palettes, no RLE encoding.
GLbyte *gltLoadPNG(int resourceId, GLint *iWidth, GLint *iHeight, GLint *iComponents, GLenum *eFormat)
{
	HRSRC		rec;
	HGLOBAL		handle;

	LPCSTR lpResourceId = MAKEINTRESOURCE(resourceId);

	rec = FindResource(NULL, lpResourceId, "PNG");
	handle = LoadResource(NULL, rec);

	MEMFILE *memfile;
	memfile = (MEMFILE *)msys_mallocAlloc(sizeof(MEMFILE));

	memfile->data = LockResource(handle);
	memfile->length = SizeofResource(NULL, rec);
	memfile->pos = 0;

	//stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)';
	int req_comp = 4;
	GLbyte* pixels = (GLbyte*)stbi_load_from_memory((unsigned char*)memfile->data, memfile->length, iWidth, iHeight, iComponents, req_comp);

	*eFormat = GL_RGBA;
	*iComponents = GL_RGBA8;

	return pixels;
}

////////////////////////////////////////////////////////////////////
// Allocate memory and load targa bits. Returns pointer to new buffer,
// height, and width of texture, and the OpenGL format of data.
// Call free() on buffer when finished!
// This only works on pretty vanilla targas... 8, 24, or 32 bit color
// only, no palettes, no RLE encoding.
GLbyte *gltLoadTGA(int resourceId, GLint *iWidth, GLint *iHeight, GLint *iComponents, GLenum *eFormat)
{
	HRSRC		rec;
	HGLOBAL		handle;

	LPCSTR lpResourceId = MAKEINTRESOURCE(resourceId);

	rec = FindResource(NULL, lpResourceId, "TGA");
	handle = LoadResource(NULL, rec);

	MEMFILE *memfile;
	memfile = (MEMFILE *)msys_mallocAlloc(sizeof(MEMFILE));

	memfile->data = LockResource(handle);
	memfile->length = SizeofResource(NULL, rec);
	memfile->pos = 0;

	TGAHEADER tgaHeader;		// TGA file header
	unsigned long lImageSize;		// Size in bytes of image
	short sDepth;			// Pixel depth;
	GLbyte	*pBits = NULL;          // Pointer to bits

	// Default/Failed values
	*iWidth = 0;
	*iHeight = 0;
	*eFormat = GL_BGR_EXT;
	*iComponents = GL_RGB8;

	// Read in header (binary)
	memread(&tgaHeader, 18, (unsigned int)memfile);

	// Do byte swap for big vs little endian
#ifdef __APPLE__
	LITTLE_ENDIAN_WORD(&tgaHeader.colorMapStart);
	LITTLE_ENDIAN_WORD(&tgaHeader.colorMapLength);
	LITTLE_ENDIAN_WORD(&tgaHeader.xstart);
	LITTLE_ENDIAN_WORD(&tgaHeader.ystart);
	LITTLE_ENDIAN_WORD(&tgaHeader.width);
	LITTLE_ENDIAN_WORD(&tgaHeader.height);
#endif


	// Get width, height, and depth of texture
	*iWidth = tgaHeader.width;
	*iHeight = tgaHeader.height;
	sDepth = tgaHeader.bits / 8;

	// Put some validity checks here. Very simply, I only understand
	// or care about 8, 24, or 32 bit targa's.
	if(tgaHeader.bits != 8 && tgaHeader.bits != 24 && tgaHeader.bits != 32)
		return NULL;

	// Calculate size of image buffer
	lImageSize = tgaHeader.width * tgaHeader.height * sDepth;

	// Allocate memory and check for success
	pBits = (GLbyte*)msys_mallocAlloc(lImageSize);
	if(pBits == NULL)
		return NULL;

	// Read in the bits
	// Check for read error. This should catch RLE or other 
	// weird formats that I don't want to recognize
	if(memread(pBits, lImageSize, (unsigned int)memfile) ==0)
	{
		msys_mallocFree(pBits);
		return NULL;
	}

	// Set OpenGL format expected
	switch(sDepth)
	{
	case 3:     // Most likely case
		*eFormat = GL_BGR_EXT;
		*iComponents = GL_RGB8;
		break;
	case 4:
		*eFormat = GL_BGRA_EXT;
		*iComponents = GL_RGBA8;
		break;
	case 1:
		*eFormat = GL_LUMINANCE;
		*iComponents = GL_LUMINANCE8;
		break;
	};


	// Done with File
	memclose((unsigned int)memfile);

	// Return pointer to image data
	return pBits;
}

void ViewOrtho(GLdouble width, GLdouble height)                            // Set Up An Ortho View
{
	glMatrixMode(GL_PROJECTION);                    // Select Projection
	glPushMatrix();                         // Push The Matrix
	glLoadIdentity();                       // Reset The Matrix
	glOrtho( 0, width , height , 0, -1, 1 );             // Select Ortho Mode (ie.640x480)
	glMatrixMode(GL_MODELVIEW);                 // Select Modelview Matrix
	glPushMatrix();                         // Push The Matrix
	glLoadIdentity();                       // Reset The Matrix
}

void ViewPerspective()                          // Set Up A Perspective View
{
	glMatrixMode( GL_PROJECTION );                  // Select Projection
	glPopMatrix();                          // Pop The Matrix
	glMatrixMode( GL_MODELVIEW );                   // Select Modelview
	glPopMatrix();                          // Pop The Matrix
}


float ColorByteToFloat(int byteVal)
{
	return (float)byteVal / 255.0f;
}

void _glColor4f(color4f col)
{
	glColor4f(col.r, col.g, col.b, col.a);
}

void _glVertex3f(vertex *v)
{
	glVertex3f(v->x, v->y, v->z);
}

void _glTexCoord2f(vertexTexCoord *v)
{
	glTexCoord2f(v->u, v->v);
}

void _glTri(vertex *v1, vertex *v2, vertex *v3)
{
	_glVertex3f(v1);
	_glVertex3f(v2);
	_glVertex3f(v3);
}

void _glQuad(vertex *v1, vertex *v2, vertex *v3, vertex *v4)
{
	_glVertex3f(v1);
	_glVertex3f(v2);
	_glVertex3f(v3);
	_glVertex3f(v4);
}

void _glNormal3f(vertex *v)
{
	glNormal3f(v->x, v->y, v->z);
}


void RotateVertexX(vertex *pInput, vertex *pOutput, float Angle)
{
	float tempy;
	vertex Out;

	tempy = pInput->y * cosf (Angle) - pInput->z * sinf (Angle);
	Out.z = pInput->y * sinf (Angle) + pInput->z * cosf (Angle);
	Out.x = pInput->x;
	Out.y = tempy;

	*pOutput = Out;
}
void RotateVertexY(vertex *pInput, vertex *pOutput, float Angle)
{
	float tempx;
	vertex Out;

	tempx = pInput->x * cosf (Angle) - pInput->z * sinf (Angle);
	Out.z = pInput->x * sinf (Angle) + pInput->z * cosf (Angle);
	Out.x = tempx;
	Out.y = pInput->y;

	*pOutput = Out;
}

void RotateVertexZ(vertex *pInput, vertex *pOutput, float Angle)
{
	float tempx;
	vertex Out;

	tempx = (pInput->x * cosf (Angle) - pInput->y * sinf (Angle));
	Out.y = (pInput->x * sinf (Angle) + pInput->y * cosf (Angle));
	Out.x = tempx;
	Out.z = pInput->z;

	*pOutput = Out;
}

void RotateVertexXYZ(vertex *pInput, vertex *pOutput, float AngleX, float AngleY, float AngleZ)
{
	vertex Out;
	RotateVertexX(pInput, &Out, AngleX);
	RotateVertexY(&Out, pOutput, AngleY);
	RotateVertexZ(pOutput, &Out, AngleZ);
	*pOutput = Out;
}

void PerspectiveCorrect(vertex *pInput, int distance, int lens, int xScale, int yScale)
{
	pInput->x = ( lens*pInput->x / (distance-pInput->z))*xScale;
	pInput->y = ( lens*pInput->y / (distance-pInput->z))*yScale;
	pInput->z = ((pInput->z + 2) * 0xFFFFFFF);
	//pInput->sz = 0;
}

void normalize(vertex *vect)	//scales a vector a length of 1
{
	float length;

	length=sqrt(					//A^2 + B^2 + C^2 = length^2
		(vect->x*vect->x)+
		(vect->y*vect->y)+
		(vect->z*vect->z)
		);

	vect->x/=length;
	vect->y/=length;
	vect->z/=length;
}

void crossProduct(vertex *c, float a[3], float b[3])  //finds the cross product of two vectors
{  
	c->x=a[1]*b[2] - b[1]*a[2];
	c->y=a[2]*b[0] - b[2]*a[0];
	c->z=a[0]*b[1] - b[0]*a[1];
}


// calculate the normal for any face given three points in clockwise order
void GetFaceNormal(vertex *normal , vertex *v1, vertex *v2, vertex *v3)
{
	float vect[2][3];
	int a,b;
	float point[3][3];

	normal->x = 0;
	normal->y = 0;
	normal->z = 0;

	//copies points into point[][]
	point[0][0]=v1->x;    
	point[1][0]=v2->x; 
	point[2][0]=v3->x;
	point[0][1]=v1->y;    
	point[1][1]=v2->y; 
	point[2][1]=v3->y;
	point[0][2]=v1->z;    
	point[1][2]=v2->z; 
	point[2][2]=v3->z;

	for (a=0;a<2;++a)        //calculates vectors from point[0] to point[1]
	{                        //and point[0] to point[2]
		for (b=0;b<3;++b)
		{
			vect[a][b]=point[2-a][b]-point[0][b];      
		}
	}

	crossProduct(normal,vect[0],vect[1]);               //calculates vector at 90° to to 2 vectors
	//normalize(normal);                                  //makes the vector length 1*/
}


void populateVertex(vertex* v, float x, float y, float z)
{
	v->x = x;
	v->y = y;
	v->z = z;
}

void copyVertex(vertex* vDest, vertex* vSource)
{
	vDest->x = vSource->x;
	vDest->y = vSource->y;
	vDest->z = vSource->z;
}

void addVertex(vertex* vDest, vertex* vSource)
{
	vDest->x += vSource->x;
	vDest->y += vSource->y;
	vDest->z += vSource->z;
}

void divVertex(vertex* vDest, float amount)
{
	vDest->x /= amount;
	vDest->y /= amount;
	vDest->z /= amount;
}

void multVertex(vertex* vDest, float amount)
{
	vDest->x *= amount;
	vDest->y *= amount;
	vDest->z *= amount;
}

void blendVertex(vertex* vDest, vertex *v1, vertex *v2, float amount)
{
	vDest->x = (v2->x * amount) + (v1->x * (1.0f-amount));
	vDest->y = (v2->y * amount) + (v1->y * (1.0f-amount));
	vDest->z = (v2->z * amount) + (v1->z * (1.0f-amount));
}

void invertVertex(vertex* v)
{
	v->x = -v->x;
	v->y = -v->y;
	v->z = -v->z;
}

void LogGlError()
{
	GLenum e = glGetError();
	if (e>0)
	{
		msys_debugPrintf("GlError: %d\n", e);
	}
}

void lerpVertex(vertex* startVertex, vertex* endVertex, vertex* out, float percent)
{
	out->x = startVertex->x + percent*(endVertex->x - startVertex->x);
	out->y = startVertex->y + percent*(endVertex->y - startVertex->y);
	out->z = startVertex->z + percent*(endVertex->z - startVertex->z);
}


