#include <windows.h>
#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include "atglib.h"			// Ile's brilliant texgen library
#include "system.h"
#include "globals.h"
#include "keys.h"
#include "textures.h"
#include "sound.h"
#include "text.h"
#include "lzw.h"
#include "objgen.h"
#include "terrain.h"
#include "temple.h"
#include "plasma.h"
#include "vector.h"
#include "golden.h"
#include "nedata.h"
#include "resource.h"




//------------------------------------------------------------------>
// Function		:	Init
// Description	:	All initialization is done here, in the end it
//					set the demostate variable to 1, to allow other
//					functions(mostly DoRedraw) to know that the init
//					is done.
//------------------------------------------------------------------>
void Init()
{
	glShadeModel (GL_SMOOTH);
	glEnable (GL_LIGHTING);
	glEnable (GL_DEPTH_TEST);
	glEnable (GL_LIGHT0);
	glEnable (GL_CULL_FACE);
	glEnable (GL_BLEND);
	glEnable (GL_FOG);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
	glTexGenf (GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
	glTexGenf (GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
    glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
	glCullFace (GL_BACK);
	glClearColor (0.0, 0.0f, 0.0f, 1.0f);
	glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
	glEnable (GL_COLOR_MATERIAL);

	// Parameters for black, linear fog
	GLfloat fogCol[4] = { 0.0, 0.0f, 0.0f, 1.0f };
	glFogfv (GL_FOG_COLOR, fogCol);
	glFogi (GL_FOG_MODE, GL_LINEAR);
	glFogf (GL_FOG_START, 200.0);
	glFogf (GL_FOG_END, 400.0);

	InitLZW ();
	
	InitGLText ();
	LoadTextures ();
	SoundInit ();
	DeInitLZW ();		// we won't be needing this anymore

	init_plasma();

	cube	= new Model(MESH_QUAD);
	esher	= new Model;
	plane	= new Model;
	sphere	= new Model;
	sphere2	= new Model;
	terrain = new Model;

	char *tdata = new char[256*256];
	memset (tdata, 0, 256*256*sizeof(char));

	genCube (*cube);
	genEsher (*esher, 25, 40, 25, 60, 25);
	genSphere (*sphere, 100.0, 40, 10, false);
	genSphere (*sphere2, 100.0, 40, 10, false);
	genTerrain (*plane, 51, tdata, 20.0);
	genTerrainData (tdata);
	genTerrain (*terrain, 51, tdata, 20.0);

	nelogomodel = new Model;
	loadModel(*nelogomodel,nedata,78,136);
	nelogomodel->textured = false;

	generate_Temple();
	generate_Golden();

	cube->textured = false;
	esher->textured = true;
	sphere->textured = true;
	sphere2->textured = true;
	terrain->textured = false;
	cube->texture = TID_ATG[3]; //1
	esher->texture = TID_PLASTIK;
	sphere->texture = TID_ATG[0];
	sphere2->texture = TID_ATG[3];
	terrain->texture = TID_ATG[2];
	delete tdata;

	
	
	cam.directional = false;
	cam.pos[0] = 0;
	cam.pos[1] = 0;
	cam.pos[2] = 50;

	cam.tgt[0] = 0;
	cam.tgt[1] = 0;
	cam.tgt[2] = 0;

	demostate = 1;		// this means the init is done
	part = 0;
	FMUSIC_PlaySong(mod);
}

//------------------------------------------------------------------>
// Function		:	DeInit
// Description	:	Frees memory, changes the resolutions back etc.
//------------------------------------------------------------------>
void DeInit ()
{
	int i=0;

	SoundKill ();
	if (fullscreen)
		ChangeRes (oldXRES, oldYRES, 16, 0);

	for (i = 0; i < 8; i++)
		delete b[i];

	delete cube;
	delete esher;
	delete sphere;
	delete sphere2;
	delete terrain;
	
	for (i = 0; i < ntemplemodels; i++)
		delete templesubmodel[i]; 
}

//------------------------------------------------------------------>
// Function		:	MakeAlpha
// Description	:	Adds an alpha value of 0.5(128 or 0x80) to a texture
//------------------------------------------------------------------>
void MakeAlpha (int *data, int size)
{
	for (int i = 0; i < size; i++)
	{
		unsigned char r, g, b, a;

		b =  data[i] & 0xFF;
		g = (data[i] >> 8) & 0xFF;
		r = (data[i] >> 16) & 0xFF;
		a =  data[i] >> 24;

		a = 0x80;

		data[i] = (a << 24) | (b << 16) | (g << 8) | r;
	}
}

void FakeAlpha (int *data, int size)
{
	for (int i = 0; i < size; i++)
	{
		unsigned char r, g, b, a;

		r =  data[i] & 0xFF;
		g = (data[i] >> 8) & 0xFF;
		b = (data[i] >> 16) & 0xFF;
		a =  data[i] >> 24;

		a = (unsigned char)((r + g + b) / 3.0);
		if (a < 255)
		{
			r = 0xFF;
			g = 0xFF;
			b = 0xFF;
		}

		data[i] = (a << 24) | (b << 16) | (g << 8) | r;
	}
}


//------------------------------------------------------------------>
// Function		:	ChangeRes
// Description	:	Changes the windows resolution to x*y in
//					[bpp]-bit colormode. Use CDS_FULLSCREEN as a flag
//					for fullscreen mode.
//------------------------------------------------------------------>
void ChangeRes (int x, int y, int bpp, int flags)
{
	DEVMODE md;

	memset(&md, 0, sizeof(md));
	md.dmSize = sizeof(md);
	md.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
	md.dmPelsWidth	= x;
	md.dmPelsHeight = y;
	md.dmBitsPerPel = bpp;
	ChangeDisplaySettings (&md, flags);
}

//------------------------------------------------------------------>
// Function		:	Resize
// Description	:	Called if we are in windowed mode and someone(user)
//					resizes the window.
//------------------------------------------------------------------>
void Resize ()
{
	glViewport(0, 0, XRES, YRES);
	SetProjection();
}

//------------------------------------------------------------------>
// Function		:	SetupPixelFormat
// Description	:	Sets up the correct pixel format for
//					a DC(called at init)
//------------------------------------------------------------------>
int SetupPixelFormat(HDC hDC)
{
	int SelectedPixelFormat;
	BOOL retVal;
	PIXELFORMATDESCRIPTOR pfd =
	{
		sizeof(PIXELFORMATDESCRIPTOR),	// size of this pfd
		1,								// version num
		PFD_SUPPORT_OPENGL,				// support OpenGL
		PFD_TYPE_RGBA,					// pixel type
		0,								// 8-bit color depth
		0, 0, 0, 0, 0, 0,				// color bits (ignored)
		0,								// no alpha buffer
		0,								// alpha bits (ignored)
		0,								// no accumulation buffer
		0, 0, 0, 0,						// accum bits (ignored)
		0,								// depth buffer
		0,								// no stencil buffer
		0,								// no auxiliary buffers
		PFD_MAIN_PLANE,					// main layer
		0,								// reserved
		0, 0, 0,						// no layer, visible, damage masks
	};

	pfd.cColorBits = GetDeviceCaps (hDC, BITSPIXEL);
	pfd.iPixelType = PFD_TYPE_RGBA;
	pfd.dwFlags |= PFD_DOUBLEBUFFER;
	pfd.dwFlags |= PFD_DRAW_TO_WINDOW;

	SelectedPixelFormat = ChoosePixelFormat (hDC, &pfd);
	if (SelectedPixelFormat == 0)
	{
		MessageBox(WindowFromDC(hDC), "Failed to find acceptable pixel format.", "OpenGL application error", MB_ICONERROR | MB_OK);
		return 0;
	}

	retVal = SetPixelFormat(hDC, SelectedPixelFormat, &pfd);
	if (retVal != TRUE)
	{
		MessageBox(WindowFromDC(hDC), "Failed to set pixel format.", "OpenGL application error", MB_ICONERROR | MB_OK);
		return 0;
	}
	return 1;
}


//------------------------------------------------------------------>
// Function		:	SetProjection
// Description	:	Called at the start of every frame to set up the
//					viewport. Not that eye, look and fov are globals.
//					Look is not a direction vector, but a point that
//					the camera will look at.
//------------------------------------------------------------------>
void SetProjection ()
{
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity ();
	gluPerspective (fov, 4.0/3.0, 0.2, 1000.0);

	glMatrixMode (GL_MODELVIEW);
	glLoadIdentity ();

	cam.use ();
}

//------------------------------------------------------------------>
// Function		:	DrawOverlays
// Description	:	Draws any 2D overlays before flipping the buffers.
//					(ok, all it draws is an NE crown =D )
//------------------------------------------------------------------>
void DrawOverlays ()
{
	glPushAttrib (GL_ALL_ATTRIB_BITS);
	glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
	glDisable (GL_LIGHTING);
	glDisable (GL_TEXTURE_GEN_S);
	glDisable (GL_TEXTURE_GEN_T);
	glEnable (GL_TEXTURE_2D);
	glDisable (GL_BLEND);
	glDisable (GL_DEPTH_TEST);

	glMatrixMode (GL_PROJECTION);
	glPushMatrix ();
	glLoadIdentity ();
	glOrtho (0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f);

	glMatrixMode (GL_MODELVIEW);
	glPushMatrix ();
	glLoadIdentity ();

	glBindTexture (GL_TEXTURE_2D, TID_TEXT);
	glColor4f (1.0f, 1.0f, 1.0f, 1.0f); 
	glBegin (GL_QUADS);
		glTexCoord2f((GLfloat)(0.0000),	(GLfloat)(0.8652));	glVertex2f (0.03f, 0.87f);
		glTexCoord2f((GLfloat)(0.0000),	(GLfloat)(1.0000));	glVertex2f (0.03f, 0.97f);
		glTexCoord2f((GLfloat)(0.1348),	(GLfloat)(1.0000));	glVertex2f (0.10f, 0.97f);
		glTexCoord2f((GLfloat)(0.1348),	(GLfloat)(0.8652));	glVertex2f (0.10f, 0.87f);
	glEnd ();
	glPopMatrix ();

	glMatrixMode (GL_PROJECTION);
	glPopMatrix ();
	glMatrixMode (GL_MODELVIEW);

	glPopAttrib ();
}

//------------------------------------------------------------------>
// Function		:	EndFrame
// Description	:	Called in the end of every frame. Draws overlays,
//					flushes all GL commands and flips the double buffer.
//------------------------------------------------------------------>
void EndFrame (HDC *hDC)
{
/*
	static GLfloat last;
	GLfloat fps = 1.0f / (time - last);
	last = time;
	char ctemp[10];
	glText2D (_gcvt(fps, 2, ctemp), 0.1, 0.1, 0.3);
*/

	//DrawOverlays ();

	glFlush();
	SwapBuffers(*hDC);
}

//------------------------------------------------------------------>
// Function		:	ATGCallback
// Description	:	Called by ATGlib during texture calculation with
//					f ranging from 0 to 1.
//					I then use the value to do a progress meter.
//------------------------------------------------------------------>
void __stdcall ATGCallback(float f)
{
	char percent[10];

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	SetProjection ();

	wsprintf (percent, "%d%%", (int)(f*100.0));
	glText2D ("[Generating Some Textures]", 0.15f, 0.45f, 0.35f);
	glText2D ("{", 0.1f, 0.55f, 0.35f);
	glText2D ("}", 0.87f, 0.55f, 0.35f);
	glText2D (percent, 0.45f, 0.7f, 0.35f);
	for (int i = 0; i < (int)(f*20.0f);i++)
		glText2D (".", i*0.035f + 0.15f, 0.55f, 0.35f);

	EndFrame (&hDC);
}

//------------------------------------------------------------------>
// Function		:	UploadTexture
// Description	:	Uploads the texture in data with texture ID tID
//					to the gfxmemory.
//------------------------------------------------------------------>
void UploadTexture (int *data, int tID, int width, int height)
{   
	glBindTexture (GL_TEXTURE_2D, tID);
	glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glTexImage2D (GL_TEXTURE_2D, 0, 4, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
	//gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_RGB, GL_UNSIGNED_BYTE, data);
}


//------------------------------------------------------------------>
// Function		:	MakePlastic
// Description	:	Generates a texture for plastic spheremapping.
//------------------------------------------------------------------>
void MakePlastic (int *texture, int hicol)
{
	for (int x = 0; x < 256; x++)
	{
		for (int y = 0; y < 256; y++)
		{
			if (sqrt((150-x)*(150-x)+(170-y)*(170-y)) < 20)
				texture[256*y+x] = hicol;
			else if (sqrt((128-x)*(128-x)+(128-y)*(128-y)) < 85)
				texture[256*y+x] = ABGR	(	ALF(hicol),
											(int)(BLU(hicol)*0.60),
											(int)(GRN(hicol)*0.60),
											(int)(RED(hicol)*0.60)	);
			else
				texture[256*y+x] = ABGR	(	ALF(hicol),
											(int)(BLU(hicol)*0.30),
											(int)(GRN(hicol)*0.30),
											(int)(RED(hicol)*0.30)	);
		}
	}
}


int *LoadPackedPic (int rID, int w, int h, bool blur)
{
	int i, j;
	int *tempdata;
	MEMFILE *data;

	data = unpack (rID, w*h/8);

	// expand from 1bit to 32bit
	int *newdata = new int[w*h]; 
	for (i = 0; i < w*h/8; i++)
	{
		for (j = 0; j < 8; j++)
		{
			if (data->data[i] & (1 << j))
				newdata[i*8+j] = 0xFFFFFFFF;
			else
				newdata[i*8+j] = 0x0;
		}
	}



   // for (i=0;i<w*h;i++)
	//	newdata[i] = 0;//(rand()%(256*256*256));// & 0x00FFFFFF;


	if (blur)
	{
		// blur the alpha channel
		tempdata = new int[w*h];
		memset (tempdata, 0, w*h*sizeof(int));
		for (int x = 1; x < w; x++)
		{
			for (int y = 1; y < h; y++)
			{
				int p[8];
				
				p[0] = newdata[w*(y-1)+x-1];
				p[1] = newdata[w*(y-1)+x];
				p[2] = newdata[w*(y-1)+x+1];
				p[3] = newdata[w*(y)  +x-1];
				p[4] = newdata[w*(y)  +x+1];
				p[5] = newdata[w*(y+1)+x-1];
				p[6] = newdata[w*(y+1)+x];
				p[7] = newdata[w*(y+1)+x+1];
				
				
				tempdata[w*y+x] = newdata[w*y+x];
				
				tempdata[w*y+x] &= 0x00FFFFFF;
				//tempdata[w*y+x] &= 0x00000000;

				if (newdata[w*y+x] != 0x0)
				{
					tempdata[w*y+x] |= ((	BLU(p[0]) + BLU(p[1]) +
											BLU(p[2]) + BLU(p[3]) +
											BLU(p[4]) + BLU(p[5]) +
											BLU(p[6]) + BLU(p[7])) / 8 ) << 24;
				}
			}
		}
	}  
	// the pixel data has been uploaded, so delete it
	delete data->data;
	delete data;

	if (blur)
	{		delete newdata;
		return tempdata;
	}
	else 
	{
		delete tempdata;
		return newdata;
	}
}

void LoadLogos ()
{
	int *data = LoadPackedPic (IDR_LOGO, 748, 87, true);
	int *data2 = new int[1024*128];

	MEMFILE *rdata;
	int *rdata2 = new int[128*128];
	int *rdata3 = new int[128*61];
	int x, y;

	rdata = unpack (IDR_RAVEN, 128*61);

	memset (data2, 0, 1024*128*sizeof(int));
	memset (rdata2, 0, 128*128*sizeof(int));

	for (int i = 0; i < 128*61; i++)
	{
		unsigned char a = rdata->data[i];

		rdata3[i] = ABGR (a, a, a, a);
	}

	for (x = 0; x < 748; x++)
	{
		for (y = 0; y < 87; y++)
		{
			data2[1024*(y+(128-87)/2)+(x+(1024-748)/2)] = data[748*y+x];
		}
	}

	// 128 x 61
	for (x = 0; x < 128; x++)
	{
		for (y = 0; y < 61; y++)
		{
			rdata2[128*(y+(128-61)/2)+(x+(128-128)/2)] = rdata3[128*y+x];
		}
	}


	FakeAlpha (data2, 1024*128);
	UploadTexture (data2, TID_LOGO, 1024, 128);

//	MakeAlpha (rdata2, 128*128);
	UploadTexture (rdata2, TID_RAVEN, 128, 128);

	delete data2;
	delete data;
	delete rdata->data;
	delete rdata;
	delete rdata2;
	delete rdata3;
}

//------------------------------------------------------------------>
// Function		:	LoadTextures
// Description	:	Called by Init, generates and uploads all the textures
//					(also adds an alpha to them by calling MakeAlpha)
//------------------------------------------------------------------>
void LoadTextures ()
{
	int i = 0;

	atgUseDisk = false;
	atgUseMmx = false;
	//unsigned int ** textures2 = atgLoadList( atgList2, ATGCallback );
	unsigned int ** textures = atgLoadList( atgList, ATGCallback );
	int *plastik = new int[256*256];

	glGenTextures (nATGtextures, TID_ATG);
	//glGenTextures (nATGtextures2, TID2_ATG);
	glGenTextures (1, &TID_PLASTIK);
	glGenTextures (1, &TID_LOGO);
	glGenTextures (1, &TID_RAVEN);
	LoadLogos ();

	for (i = 0; i < nATGtextures; i++)
	{
		MakeAlpha ((int *)textures[i], 256*256);
		UploadTexture ((int*)textures[i], TID_ATG[i], 256, 256);
		delete textures[i];
	}
/*
	for (i = 0; i < nATGtextures2; i++)
	{
		MakeAlpha ((int *)textures2[i], 256*256);
		UploadTexture ((int*)textures2[i], TID2_ATG[i], 256, 256);
		delete textures2[i];
	} */

	MakePlastic (plastik, 0x80FFFF88);
	UploadTexture (plastik, TID_PLASTIK, 256, 256);

	delete textures;
	//delete textures2;
	delete plastik;
}

