
#include <QtGui>
#include <QtOpenGL>
#include <math.h>
#include <stdio.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include "glwidget.h"
#include "gameFigure.h"


#include <QDebug>

#ifndef GL_MULTISAMPLE
#define GL_MULTISAMPLE  0x809D
#endif

// Konstruktor
GLWidget::GLWidget( QWidget* parent)
: QGLWidget( /*QGLFormat( QGL::SampleBuffers | QGL::StencilBuffer | QGL::DepthBuffer | QGL::AlphaChannel ),*/ parent)
{
	active=TRUE;									// Window Active Flag Set To TRUE By Default
	xr = 0.0f;

	markx = 0;
	markz = 0;

	//create the fonts
	BuildFont();

	//load our chess queen model
	LoadASCUV("data/queentex.asc",queen);

	//create the queen game
	queengame.NewGame();


	float xx=0.0f, zz=0.0f;
	int i,j;

	boardlist=glGenLists(1);
	glNewList(boardlist,GL_COMPILE);

	for (i=0; i<4; i++)
	{
		for (j=0; j<4; j++)
		{
			glPushMatrix();
			glTranslatef(xx,0.0f,zz);
			DrawTile();
			glPopMatrix();
			xx+=1.0f;
		}
		xx=0.0f;
		zz-=1.0f;
	}


	glEndList();

	
	textviewwidth=width();
	textviewheight=height();

	StartPos = 32;
	size = 1.0f;
}

// Destruktor
GLWidget::~GLWidget()
{
	
}


QSize GLWidget::minimumSizeHint() const
{
	return QSize(50, 50);
}

QSize GLWidget::sizeHint() const
{
	return QSize(400, 400);
}


void GLWidget::initializeGL()
{
	if (!LoadGLTextures())								// Jump To Texture Loading Routine
	{
		return;									// If Texture Didn't Load Return FALSE
	}

	glEnable(GL_TEXTURE_2D);							// Enable Texture Mapping
	glBlendFunc(GL_SRC_ALPHA,GL_ONE);					// Set The Blending Function For Translucency
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);				// This Will Clear The Background Color To Black
	glClearDepth(1.0);									// Enables Clearing Of The Depth Buffer
	glClearStencil(0);									// Clear The Stencil Buffer To 0
	glDepthFunc(GL_LESS);								// The Type Of Depth Test To Do
	glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
	glShadeModel(GL_SMOOTH);							// Enables Smooth Color Shading
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculations

	return;										// Initialization Went OK
}


void GLWidget::resizeGL( int width, int height )
{
	glViewport(0,0,width,height);						// Reset The Current Viewport

	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window
	gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glLoadIdentity();									// Reset The Modelview Matrix
}


void GLWidget::paintGL()
{
	int i,j;
	float xx=0.0f, zz=0.0f;
	double eqr[] = {0.0f,-1.0f, 0.0f, 0.0f};			// Plane Equation To Use For The Reflected Objects

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);	// Clear The Screen And The Depth Buffer
	glLoadIdentity();									// Reset The View

	//set up the camera

	glTranslatef(0.0f, 0.0f, -5.0f);
	glRotatef(25.0f,1.0f,0.0f,0.0f);
	glRotatef(xr,0.0f,1.0f,0.0f);
	glTranslatef(-2.0f,0.0f,2.0f);
	
	
	


	glColorMask(0,0,0,0);								// Set Color Mask
	glEnable(GL_STENCIL_TEST);							// Enable Stencil Buffer For "marking" The Floor
	glStencilFunc(GL_ALWAYS, 1, 1);						// Always Passes, 1 Bit Plane, 1 As Mask
	glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);			// We Set The Stencil Buffer To 1 Where We Draw Any Polygon
														// Keep If Test Fails, Keep If Test Passes But Buffer Test Fails
														// Replace If Test Passes
	glDisable(GL_DEPTH_TEST);	// Disable Depth Testing



	glBindTexture(GL_TEXTURE_2D, texture[1]);

	//draw the chessboard
	glCallList(boardlist);



	glEnable(GL_DEPTH_TEST);							// Enable Depth Testing
	glColorMask(1,1,1,1);								// Set Color Mask to TRUE, TRUE, TRUE, TRUE
	glStencilFunc(GL_EQUAL, 1, 1);						// We Draw Only Where The Stencil Is 1
														// (I.E. Where The Floor Was Drawn)
	glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);				// Don't Change The Stencil Buffer

	glEnable(GL_CLIP_PLANE0);							// Enable Clip Plane For Removing Artifacts
														// (When The Object Crosses The Floor)
	glClipPlane(GL_CLIP_PLANE0, eqr);					// Equation For Reflected Objects

	glPushMatrix();										// Push The Matrix Onto The Stack
	glScalef(1.0f, -1.0f, 1.0f);					// Mirror Y Axis

	//draw the queens
	xx=0.25f;
	zz=-0.25f;
	glBindTexture(GL_TEXTURE_2D, texture[0]);
	//check for queens on the board
	for (i=0; i<8; i++)
	{
		for (j=0; j<8; j++)
		{
			if (queengame.CheckForQueen(j,i)==1)
			{
				glPushMatrix();
				glTranslatef(xx,0.0f,zz);
				glRotatef(0.0f,0.0f,0.0f,1.0f);
				DrawAsc(queen);
				glPopMatrix();
			}
			xx+=0.50f;
		}
		xx=0.25f;
		zz-=0.5f;
	}

	glPopMatrix();										// Pop The Matrix Off The Stack
	glDisable(GL_CLIP_PLANE0);							// Disable Clip Plane For Drawing The Floor
	glDisable(GL_STENCIL_TEST);							// We Don't Need The Stencil Buffer Any More (Disable)

	glEnable(GL_BLEND);									// Enable Blending (Otherwise The Reflected Object Wont Show)

	glColor4f(1.0f, 1.0f, 1.0f, 0.6f);					// Set Color To White With 80% Alpha
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);	// Blending Based On Source Alpha And 1 Minus Dest Alpha

	//draw the chessboard again
	glBindTexture(GL_TEXTURE_2D, texture[1]);
	glCallList(boardlist);


	glBindTexture(GL_TEXTURE_2D, texture[2]);
	glDisable(GL_BLEND);								// Disable Blending

	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

	//draw queens again
	xx=0.25f;
	zz=-0.25f;
	glBindTexture(GL_TEXTURE_2D, texture[0]);
	//check for queens on the board
	for (i=0; i<8; i++)
	{
		for (j=0; j<8; j++)
		{
			if (queengame.CheckForQueen(j,i)==1)
			{
				glPushMatrix();
				glTranslatef(xx,0.0f,zz);
				glRotatef(0.0f,0.0f,0.0f,1.0f);
				DrawAsc(queen);;
				glPopMatrix();
			}
			xx+=0.50f;
		}
		xx=0.25f;
		zz-=0.5f;
	}

	
	//draw the marker queen
	glBindTexture(GL_TEXTURE_2D, texture[2]);
	glPushMatrix();
	glTranslatef(markerx+0.25f,0.0f,markerz-0.25f);
	DrawAsc(queen);

	glPopMatrix();
	glDisable(GL_BLEND);


	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

	glPrint(180,20,"Use arrow keys to move highlighted queen");
	glPrint(180,40,"Press M to mark or unmark a square");

	if (queengame.GameStatus()==1)
		glPrint(300,60,"You won!");

	return;										// Everything Went OK
}



void GLWidget::mousePressEvent(QMouseEvent* event)
{
	
}


void GLWidget::mouseMoveEvent(QMouseEvent* event)
{
	
}


void GLWidget::keyPressEvent( QKeyEvent* event )
{
	switch( event->key() )
	{
		case Qt::Key_Down:
			markerz+=0.5f;
			markz--;
			if (markerz>0.0f)
			{
				markerz-=0.5f;
				markz++;
			}
			break;

		case Qt::Key_Up:
			markerz-=0.5f;
			markz++;
			if (markerz<=-4.0f)
			{
				markerz+=0.5f;
				markz--;
			}
			break;

		case Qt::Key_Left:
			markerx-=0.5f;
			markx--;
			if (markerx<0.0f)
			{
				markerx+=0.5f;
				markx++;
			}
			break;

		case Qt::Key_Right:
			markerx+=0.5f;
			markx++;
			if (markerx>=4.0f)
			{
				markerx-=0.5f;
				markx--;
			}
			break;

		case Qt::Key_M:
			qDebug() << "X: " << markx << " Z: " << markz;
			queengame.PlaceQueen(markx,markz);
			break;


		case Qt::Key_A:
			xr-=1.0f;
			break;

		case Qt::Key_S:
			xr+=1.0f;
			break;

	}

	update();
}


//load an ASC file with texture coordinates
void GLWidget::LoadASCUV(char *file, model &m)
{
	FILE *fp;
	char junk[80];
	int num, verts, faces, i, a, b, c;
	float xval, yval, zval, uval, vval;
	fp = fopen(file,"r");

	fgets(junk,80,fp); //get first line
	fscanf(fp,"\nNamed object: %s\n",&junk);
	fscanf(fp,"Tri-mesh, Vertices: %d Faces: %d\n",&verts,&faces);
	fgets(junk,80,fp); //get next line
	fgets(junk,80,fp); //get next line
	m.numverts = verts;
	m.numfaces = faces;
	m.faces = new face[faces];
	m.verts = new vert[verts];
	//read all the vertices
	for (i=0; i<verts; i++)
	{
		fscanf(fp,"Vertex %d: X:%f Y:%f Z:%f U:%f V:%f\n",&num,&xval,&yval,&zval,&uval,&vval);
		m.verts[i].x=xval;
		m.verts[i].y=zval;
		m.verts[i].z=yval;
		m.verts[i].u=uval;
		m.verts[i].v=vval;
	}
	fgets(junk,80,fp); //get next line
	//read all the faces
	for (i=0; i<faces; i++)
	{
		int num1, num2, num3, num4;
		fscanf(fp,"Face %d: A:%d B:%d C:%d AB:%d BC:%d CA:%d\n",&num1,&a,&b,&c,&num2,&num3,&num4);
		fgets(junk,80,fp);
		fgets(junk,80,fp); //get next line
		m.faces[i].a=a;
		m.faces[i].b=b;
		m.faces[i].c=c;
	}
	m.useuv=1;
	fclose(fp);
}



//load a targa file and return the width and height information
unsigned char* GLWidget::LoadTGA(char *name, int *width, int *height) 
{
	unsigned char   rep,*data,*buffer,*ptr,info[18];
	int Width,Height,Components,Size,i,j,k,l;

	FILE *fp;
	fp = fopen(name,"rb");
	if(!fp) return 0;
	fread(&info,1,18,fp);

	//set width and height
	Width = info[12] + info[13] * 256;
	Height = info[14] + info[15] * 256;

	switch(info[16]) 
	{
		case 32:
			Components = 4;
			break;

		case 24:
			Components = 3;
			break;

		default:
			fclose(fp);

		return 0;
	}
	//compute the size of the buffer
	Size = Width * Height * Components;
	buffer = (unsigned char*)malloc(Size);
	data = (unsigned char*)malloc(Width * Height * 4);

	fseek(fp,info[0],SEEK_CUR);
	i = 0;
	ptr = buffer;

	switch(info[2]) 
	{
		case 2:     // Unmapped RGB image
			fread(buffer,1,Size,fp);
			break;

		case 10:    // Run length encoded
			while(i < Size) 
			{
				fread(&rep,1,1,fp);
				if(rep & 0x80) 
				{
					rep ^= 0x80;
					fread(ptr,1,Components,fp);
					ptr += Components;
					for(j = 0; j < rep * Components; j++) 
					{
						*ptr = *(ptr - Components);
						ptr ++;
					}
					i += Components * (rep + 1);
				}
				else 
				{
					k = Components * (rep + 1);
					fread(ptr,1,k,fp);
					ptr += k;
					i += k;
				}
			}
			break;

		default:
			fclose(fp);
			free(buffer);
			free(data);
			return 0;
	}

	for(i = 0, j = 0; i < Size; i += Components, j += 4) 
	{  // BGR -> RGBA
		data[j] = buffer[i + 2];
		data[j + 1] = buffer[i + 1];
		data[j + 2] = buffer[i];
		
		if (Components == 4)
			data[j + 3] = buffer[i + 3];
		else
		{
			if ((buffer[i+2]==0) && (buffer[i+1]==0) && (buffer[i]==0))
				data[j+3] = 0;
			else
				data[j + 3] = 255;
		}
	}
	
	if(!(info[17] & 0x20))
		for(j = 0, k = Width * 4; j < Height / 2; j ++)
			for(i = 0; i < Width * 4; i ++) 
			{
				l = data[j * k + i];
				data[j * k + i] = data[(Height - j - 1) * k + i];
				data[(Height - j - 1) * k + i] = l;
			}

	fclose(fp);
	free(buffer);
	*width = Width;
	*height = Height;
	return data;
}

int GLWidget::LoadGLTextures()                                    // Load Bitmaps And Convert To Textures
{
	int w,h;
	unsigned char *tdata;

	//queen texture
	tdata = LoadTGA("data/tex1.tga",&w,&h);
	glGenTextures(1, &texture[0]);
	glBindTexture(GL_TEXTURE_2D, texture[0]);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, w, h, GL_RGBA, GL_UNSIGNED_BYTE, tdata);
	free(tdata);

	//marble surface texture
	tdata = LoadTGA("data/marblet.tga",&w,&h);
	glGenTextures(1, &texture[1]);
	glBindTexture(GL_TEXTURE_2D, texture[1]);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, w, h, GL_RGBA, GL_UNSIGNED_BYTE, tdata);
	free(tdata);

	//red marker texture
	tdata = LoadTGA("data/mark.tga",&w,&h);
	glGenTextures(1, &texture[2]);
	glBindTexture(GL_TEXTURE_2D, texture[2]);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, w, h, GL_RGBA, GL_UNSIGNED_BYTE, tdata);
	free(tdata);

	//font texture
	tdata = LoadTGA("data/font.tga",&w,&h);
	glGenTextures(1, &fonttexture);
	glBindTexture(GL_TEXTURE_2D, fonttexture);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, w, h, GL_RGBA, GL_UNSIGNED_BYTE, tdata);
	free(tdata);

	return TRUE;
}



//draw the ASC model
void GLWidget::DrawAsc(model m)
{
	int i;
	float v1, v2, v3, u, v;
	for (i = 0; i < m.numfaces; i++)
	{
		glBegin(GL_TRIANGLES);
		glNormal3f( 1.0f, 0.0f, 1.0f);
		v1 = m.verts[m.faces[i].a].x;
		v2 = m.verts[m.faces[i].a].y;
		v3 = m.verts[m.faces[i].a].z;
		u = m.verts[m.faces[i].a].u;
		v = m.verts[m.faces[i].a].v;
		glTexCoord2f(u,v);
		glVertex3f(v1,v2,v3);
		v1 = m.verts[m.faces[i].b].x;
		v2 = m.verts[m.faces[i].b].y;
		v3 = m.verts[m.faces[i].b].z;
		u = m.verts[m.faces[i].b].u;
		v = m.verts[m.faces[i].b].v;
		glTexCoord2f(u,v);
		glVertex3f(v1,v2,v3);
		v1 = m.verts[m.faces[i].c].x;
		v2 = m.verts[m.faces[i].c].y;
		v3 = m.verts[m.faces[i].c].z;
		u = m.verts[m.faces[i].c].u;
		v = m.verts[m.faces[i].c].v;
		glTexCoord2f(u,v);
		glVertex3f(v1,v2,v3);
		glEnd();
	}

}

void GLWidget::DrawTile()
{
	glBegin(GL_QUADS);
	glNormal3f( 0.0f, 1.0f, 0.0f);
	glTexCoord2f(0.0f,0.0f);
	glVertex3f(0.0f,0.0f,0.0f);

	glTexCoord2f(1.0f,0.0f);
	glVertex3f(1.0f,0.0f,0.0f);

	glTexCoord2f(1.0f,1.0f);
	glVertex3f(1.0f,0.0f,-1.0f);

	glTexCoord2f(0.0f,1.0f);
	glVertex3f(0.0f,0.0f,-1.0f);
	glEnd();

}


GLvoid GLWidget::glPrint(GLint x, GLint y, char *string)
{
	glEnable(GL_TEXTURE_2D);

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
	glBindTexture(GL_TEXTURE_2D, fonttexture);			// Select Our Font Texture
	glDisable(GL_DEPTH_TEST);							// Disables Depth Testing
	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glPushMatrix();										// Store The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix
	glOrtho(0,textviewwidth,0,textviewheight,-1,1);							// Set Up An Ortho Screen
	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glPushMatrix();										// Store The Modelview Matrix
	glLoadIdentity();									// Reset The Modelview Matrix
	glTranslated(x,textviewheight - y,0);								// Position The Text (0,0 - Bottom Left)
	glListBase(base - StartPos);						// Choose The Font Set (0 or 1)
	glScalef( size, size, 1.0f );
	glCallLists((GLsizei)strlen(string),GL_BYTE,string);			// Write The Text To The Screen
	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glPopMatrix();										// Restore The Old Projection Matrix
	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glPopMatrix();										// Restore The Old Projection Matrix
	glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
	glDisable(GL_BLEND);
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

}


GLvoid GLWidget::BuildFont(GLvoid)								// Build Our Font Display List
{
	float	cx;											// Holds Our X Character Coord
	float	cy;											// Holds Our Y Character Coord

	base=glGenLists(256);								// Creating 256 Display Lists

	for (loop=0; loop<256; loop++)						// Loop Through All 256 Lists
	{
		cx=float(loop%16)/16.0f;						// X Position Of Current Character
		cy=float(loop/16)/16.0f;						// Y Position Of Current Character

		glNewList(base+loop,GL_COMPILE);				// Start Building A List
			glBegin(GL_QUADS);							// Use A Quad For Each Character
				glTexCoord2f(cx,1-cy-0.0625f);			// Texture Coord (Bottom Left)
				glVertex2i(0,0);						// Vertex Coord (Bottom Left)
				glTexCoord2f(cx+0.0625f,1-cy-0.0625f);	// Texture Coord (Bottom Right)
				glVertex2i(16,0);						// Vertex Coord (Bottom Right)
				glTexCoord2f(cx+0.0625f,1-cy);			// Texture Coord (Top Right)
				glVertex2i(16,16);						// Vertex Coord (Top Right)
				glTexCoord2f(cx,1-cy);					// Texture Coord (Top Left)
				glVertex2i(0,16);						// Vertex Coord (Top Left)
			glEnd();									// Done Building Our Quad (Character)
			glTranslated(10,0,0);						// Move To The Right Of The Character
		glEndList();									// Done Building The Display List
	} // Loop Until All 256 Are Built
}

GLvoid GLWidget::KillFont(GLvoid)									// Delete The Font From Memory
{
	glDeleteLists(base,256);							// Delete All 256 Display Lists
}