//	OpenGLDrawer.cpp - OpenGL drawing backend (using SDL headers).
//  ----------------------------------------------------------------------------
//	This file is part of 'The Future Will Only Be Dystopian if Capitalism
//	Remains Undefeated', a game.
//	Copyright (C) 2009  Niall Moody
//	
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
//	----------------------------------------------------------------------------

#include "OpenGLDrawer.h"
#include "ImageLoader.h"

#include "SDL.h"
#include <cmath>
#include <fstream>
#include <iostream>

#ifdef DEBUG
#include <sstream>
#endif

using namespace std;

//------------------------------------------------------------------------------
OpenGLDrawer::OpenGLDrawer():
Drawer(),
lineImage(0),
secondImage(0),
imagesMaxId(1),
usingVBO(false),
screenWidth(1024),
screenHeight(768),
viewX(0.0f),
viewY(0.0f),
viewW(1024.0f),
viewH(768.0f),
line_pngSize(144)
{
	
}

//------------------------------------------------------------------------------
OpenGLDrawer::~OpenGLDrawer()
{
	int i;

	for(i=0;i<polys.size();++i)
	{
		delete [] polys[i].xPoints;
		delete [] polys[i].yPoints;
		delete [] polys[i].xTex;
		delete [] polys[i].yTex;
	}
}

//------------------------------------------------------------------------------
bool OpenGLDrawer::initialise(uint16_t width,
							  uint16_t height,
							  bool fs,
							  int aa)
{
	GLenum err;
	bool retval = true;
	int flags = SDL_OPENGL;
	float aspectRatio = (float)height/(float)width;

	screenWidth = width;
	screenHeight = height;

	//Set OpenGL attributes (we use 32 bit colour if we can).
	if(is16Bit())
	{
		cout << "OpenGLDrawer: Display is 16bit." << endl;
		if(SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5) < 0)
			cout << "OpenGLDrawer: Could not set red pixel size to 5..." << endl;
		if(SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5) < 0)
			cout << "OpenGLDrawer: Could not set green pixel size to 5..." << endl;
		if(SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5) < 0)
			cout << "OpenGLDrawer: Could not set blue pixel size to 5..." << endl;
	}
	else
	{
		cout << "OpenGLDrawer: Display is 32bit." << endl;
		if(SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8) < 0)
			cout << "OpenGLDrawer: Could not set red pixel size to 8..." << endl;
		if(SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8) < 0)
			cout << "OpenGLDrawer: Could not set green pixel size to 8..." << endl;
		if(SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8) < 0)
			cout << "OpenGLDrawer: Could not set blue pixel size to 8..." << endl;
		if(SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8) < 0)
			cout << "OpenGLDrawer: Could not set alpha pixel size to 8..." << endl;
	}
	if(SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16) < 0)
		cout << "OpenGLDrawer: Could not set depth size to 16..." << endl;
	if(SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1) < 0)
		cout << "OpenGLDrawer: Could not set double buffering..." << endl;
	if(SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1) < 0)
		cout << "OpenGLDrawer: Could not set vertical sync..." << endl;
	if(aa > 0)
	{
		if(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1) < 0)
			cout << "OpenGLDrawer: Could not set multi sample buffers..." << endl;
		if(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, aa) < 0)
			cout << "OpenGLDrawer: Could not set multi sample samples..." << endl;
	}

	if(fs)
		flags |= SDL_FULLSCREEN;

	//Construct window etc.
	if(SDL_SetVideoMode(width, height, 0, flags) == 0)
	{
#ifdef DEBUG
		cout << "SDLDrawer: Could not set video mode: ";
		cout << SDL_GetError() << endl;
#endif
		retval = false;
	}
	else
		retval = true;

	//Initialise GLEW.
	err = glewInit();
	if(err != GLEW_OK)
		cout << "GLEW error: " << glewGetErrorString(err) << endl;
	else
		cout << "Using GLEW version: " << glewGetString(GLEW_VERSION) << endl;

	//Initialise the fonts.
	initFonts();

	//First setup the full window as a viewport.
	glViewport(0, 0, width, height);

	//Clear to black (if we're widescreen we'll have black bars at the sides).
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT);

	//If we're widescreen, set the viewport to be a centred 4:3 aspect ratio.
	if(aspectRatio != 0.75f)
	{
		if(aspectRatio < 0.75f)
		{
			viewW = (4.0f/3.0f) * height;
			viewH = height;
			viewX = (width-viewW)*0.5f;
			viewY = 0;
		}
		else if(aspectRatio > 0.75f) //Seems unlikely, but we might as well
		{							 //cover it...
			viewW = width;
			viewH = width * 0.75f;
			viewX = 0.0f;
			viewY = (height-viewH)*0.5f;
		}

#ifdef DEBUG
		cout << "OpenGLDrawer: Setting viewport to: " << x << "x";
		cout << y << " : " << w << "x" << h << endl;
#endif

		glViewport((int)viewX, (int)viewY, (int)viewW, (int)viewH);
	}
	else
	{
		viewW = width;
		viewH = height;
	}

	//Now set up everything else.
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	//So our co-ordinates for drawing are 0-1024 (x) and 768-0 (y).
	gluOrtho2D(0.0f, 1024.0f, 768.0f, 0.0f);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glShadeModel(GL_SMOOTH);
	glClearDepth(1.0f);
	glDisable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	glEnable(GL_TEXTURE_2D);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);

	//Load the line image.
	{
		ImageLoader imLoad;
		ImageData data;

		data = imLoad.loadImage(line_png, line_pngSize);
		lineImage = loadImage(data.data, data.width, data.height);
	}

	return retval;
}

//------------------------------------------------------------------------------
uint32_t OpenGLDrawer::loadImage(uint8_t *data, uint32_t width, uint32_t height)
{
	Image tempIm;
	uint32_t retval = 0;

	if(data)
	{
		//If it's not a power-of-two-sized image, we need to make it so.
		if((!isPowerOfTwo(width)) || (!isPowerOfTwo(height)))
		{
			uint32_t i;
			uint32_t x, y;
			uint8_t tempint;
			const uint32_t w = powerOfTwo(width);
			const uint32_t h = powerOfTwo(height);
			const uint32_t dataSize = w*h*4;
			uint8_t *tempData = new uint8_t[dataSize];

			//Blank the new image.
			for(i=0;i<dataSize;++i)
				tempData[i] = 0;

			//Copy the old image into the upper left corner.
			i = 0;
			for(y=0;y<height;++y)
			{
				for(x=0;x<(width*4);++x)
				{
					tempData[x+(y*w*4)] = data[i];
					++i;
				}
				//Copy the previous value along to fill out the texture.
				tempint = data[i-1];
				for(x=(width*4);x<(w*4);++x)
					tempData[x+(y*w*4)] = tempint;
			}
			//Copy the previous value along to fill out the texture.
			i -= (width*4); //So we're back to reading the previous line.
			for(x=0;x<(w*4);++x)
			{
				tempint	= data[i];
				for(y=height;y<h;++y)
					tempData[x+(y*w*4)] = tempint;
				++i;
			}

			//Inform tempIm of the dimensions.
			tempIm.width = width;
			tempIm.height = height;
			tempIm.texWidth = (float)width/(float)w;
			tempIm.texHeight = (float)height/(float)h;

#ifdef DEBUG
			cout << "OpenGLDrawer: Image not power-of-two: " << (unsigned int)width << "x" << (unsigned int)height << endl;
			cout << "\tTexture: " << (unsigned int)w << "x" << (unsigned int)h << endl;
#endif

			//Delete the old data.
			delete [] data;
			//Make sure data now points to the new data.
			data = tempData;
		}
		else
		{
			tempIm.width = width;
			tempIm.height = height;
			tempIm.texWidth = 1.0f;
			tempIm.texHeight = 1.0f;
		}

		//Create the texture.
		glGenTextures(1, &(tempIm.texture));

		glBindTexture(GL_TEXTURE_2D, tempIm.texture);

		glTexImage2D(GL_TEXTURE_2D,
					 0,
					 GL_RGBA,
					 powerOfTwo(tempIm.width),
					 powerOfTwo(tempIm.height),
					 0,
					 GL_RGBA,
					 GL_UNSIGNED_BYTE,
					 data);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

		glBindTexture(GL_TEXTURE_2D, 0);

		//Get rid of the image data.
		delete [] data;

		//Add tempIm to our list of images.
		while(images.find(imagesMaxId) != images.end())
			++imagesMaxId;
		retval = imagesMaxId;

		images.insert(make_pair(retval, tempIm));
	}
#ifdef DEBUG
	else
		cout << "OpenGLDrawer::loadImage(): No data passed in!" << endl;
#endif

	return retval;
}

//------------------------------------------------------------------------------
void OpenGLDrawer::loadGlyph(Glyph g, uint8_t *data, uint32_t size)
{
	Image tempIm;

#ifdef DEBUG
	if(!isPowerOfTwo(size))
	{
		cout << "OpenGLDrawer: Font glyph is not power-of-two!  Not loading...";
		cout << endl;
	}
#endif

	if(data)
	{
		tempIm.width = size;
		tempIm.height = size;
		tempIm.texWidth = 1.0f;
		tempIm.texHeight = 1.0f;

		//Create the texture.
		glGenTextures(1, &(tempIm.texture));

		glBindTexture(GL_TEXTURE_2D, tempIm.texture);

		glTexImage2D(GL_TEXTURE_2D,
					 0,
					 GL_RGBA,
					 tempIm.width,
					 tempIm.height,
					 0,
					 GL_RGBA,
					 GL_UNSIGNED_BYTE,
					 data);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

		//Get rid of the existing glyph texture.
		glDeleteTextures(1, &(glyphs[g].texture));

		//Copy across the new one.
		glyphs[g].texture = tempIm.texture;
		glyphs[g].width = tempIm.width;
		glyphs[g].height = tempIm.height;
		glyphs[g].texWidth = tempIm.texWidth;
		glyphs[g].texHeight = tempIm.texHeight;
	}
#ifdef DEBUG
	else
		cout << "OpenGLDrawer::loadGlyph(): No data passed in!" << endl;
#endif
}

//------------------------------------------------------------------------------
void OpenGLDrawer::releaseImage(uint32_t id)
{
	map<uint32_t, Image>::iterator it = images.find(id);

	if(it != images.end())
	{
		glDeleteTextures(1, &(it->second.texture));
		images.erase(it);
	}
#ifdef DEBUG
	else
		cout << "OpenGLDrawer: Could not release image: " << (unsigned int)id << endl;
#endif
}

//------------------------------------------------------------------------------
uint32_t OpenGLDrawer::loadPoly(float *points, int numPoints)
{
	int i;
	Polygon p;
	float avgX = 0.0f;
	float avgY = 0.0f;
	float left = 10000.0f;
	float right = -10000.0f;
	float top = 10000.0f;
	float bottom = -10000.0f;

	p.xPoints = new float[numPoints+1];
	p.yPoints = new float[numPoints+1];
	p.xTex = new float[numPoints+1];
	p.yTex = new float[numPoints+1];
	p.numPoints = numPoints+1;

	for(i=0;i<numPoints;++i)
	{
		avgX += points[(i*2)];
		avgY += points[(i*2)+1];

		p.xPoints[i+1] = points[(i*2)];
		p.yPoints[i+1] = points[(i*2)+1];

		if(p.xPoints[i+1] < left)
			left = p.xPoints[i+1];
		else if(p.xPoints[i+1] > right)
			right = p.xPoints[i+1];

		if(p.yPoints[i+1] < top)
			top = p.yPoints[i+1];
		else if(p.yPoints[i+1] > bottom)
			bottom = p.yPoints[i+1];
	}
	avgX /= (float)numPoints;
	avgY /= (float)numPoints;

	p.xPoints[0] = avgX;
	p.yPoints[0] = avgY;

	for(i=0;i<p.numPoints;++i)
	{
		p.xTex[i] = (p.xPoints[i] - left)/(right-left);
		p.yTex[i] = (p.yPoints[i] - top)/(bottom-top);
	}

	polys.push_back(p);

	return polys.size()-1;
}

//------------------------------------------------------------------------------
uint32_t OpenGLDrawer::loadShader(const string& vertex, const string& fragment)
{
	uint32_t vertexObject;
	uint32_t fragmentObject;
	uint32_t retval = 0;

	//Create the shader objects.
	vertexObject = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
	fragmentObject = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);

	//Read the shaders from their files.
	string vertexStr = loadShaderFile(vertex);
	string fragmentStr = loadShaderFile(fragment);
	const char *vertexText = vertexStr.c_str();
	const char *fragmentText = fragmentStr.c_str();

	//Read the shader source into the shader objects.
	glShaderSourceARB(vertexObject, 1, &vertexText, NULL);
	glShaderSourceARB(fragmentObject, 1, &fragmentText, NULL);

	//Compile the shaders.
	glCompileShaderARB(vertexObject);
	glCompileShaderARB(fragmentObject);

	//Create the program object.
	retval = glCreateProgramObjectARB();

	//Attach the shaders to the program.
	glAttachObjectARB(retval, vertexObject);
	glAttachObjectARB(retval, fragmentObject);

	//Finally, link the program.
	glLinkProgramARB(retval);

	//Output the info log.
	cout << "OpenGLDrawer Shader InfoLog:" << endl;
	cout << "----------------------------" << endl;
	cout << vertex << ":" << endl;
	printInfoLog(vertexObject);
	cout << fragment << ":" << endl;
	printInfoLog(fragmentObject);

	//Get the texture variable locations.
	tex1Loc = glGetUniformLocationARB(retval, "main");
	tex2Loc = glGetUniformLocationARB(retval, "second");

	return retval;
}

//------------------------------------------------------------------------------
void OpenGLDrawer::beginDrawing()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
}

//------------------------------------------------------------------------------
void OpenGLDrawer::endDrawing()
{
	SDL_GL_SwapBuffers();
	secondImage = 0;
}

//------------------------------------------------------------------------------
unsigned char *OpenGLDrawer::getScreenPixels()
{
	unsigned char *retval = new unsigned char[screenWidth*screenHeight*3];

	//So we don't have to call this after Drawer::endDrawing().
	SDL_GL_SwapBuffers();

	memset(retval, (screenWidth*screenHeight*3), 0);
	//Get pixels from opengl frame buffer.
	glReadPixels(0,					//X start pos.
				 0,					//Y start pos.
				 screenWidth,		//Width.
				 screenHeight,		//Height.
				 GL_RGB,			//What data we're reading.
				 GL_UNSIGNED_BYTE,	//Format to write to.
				 (void *)retval);	//Buffer to write to.

	return retval;
}

//------------------------------------------------------------------------------
void OpenGLDrawer::useShader(uint32_t shader)
{
	glUseProgramObjectARB(shader);
}

//------------------------------------------------------------------------------
void OpenGLDrawer::useSecondImage(uint32_t image)
{
	secondImage = image;
}

//------------------------------------------------------------------------------
void OpenGLDrawer::drawImage(uint32_t id,
							 int16_t x,
							 int16_t y,
							 float width,
							 float height,
							 float r,
							 float g,
							 float b,
							 float a)
{
	Image *image;
	float tempX, tempY;
	map<uint32_t, Image>::iterator it;

	it = images.find(id);

	if(it != images.end())
	{
		image = &(it->second);
		if(!usingVBO)
		{
			tempX = -(((float)image->width * width) - (float)image->width);
			tempX *= 0.5f;
			tempX += (float)x;

			tempY = -(((float)image->height * height) - (float)image->height);
			tempY *= 0.5f;
			tempY += (float)y;

			glBindTexture(GL_TEXTURE_2D, image->texture);

			glColor4f(r, g, b, a);
			glBegin(GL_QUADS);
			{
				glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 0.0f);
				glVertex2f(tempX,
						   tempY);

				glMultiTexCoord2fARB(GL_TEXTURE0_ARB, image->texWidth, 0.0f);
				glVertex2f(tempX+((float)image->width * width),
						   tempY);

				glMultiTexCoord2fARB(GL_TEXTURE0_ARB,
									 image->texWidth,
									 image->texHeight);
				glVertex2f(tempX+((float)image->width * width),
						   tempY+((float)image->height * height));

				glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, image->texHeight);
				glVertex2f(tempX,
						   tempY+((float)image->height * height));
			}
			glEnd();
			glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

			glBindTexture(GL_TEXTURE_2D, 0);
		}
	}
#ifdef DEBUG
	else
	{
		cout << "OpenGLDrawer::drawImage(): Could not find image with id: ";
		cout << (unsigned int)id << endl;
	}
#endif
}

//------------------------------------------------------------------------------
void OpenGLDrawer::drawPoly(uint32_t polyId,
							uint32_t textureId,
							int16_t x,
							int16_t y,
							float width,
							float height,
							float r,
							float g,
							float b,
							float a)
{
	int i;
	Polygon p;
	Image *image;
	float tempX, tempY;
	map<uint32_t, Image>::iterator it, it2;

	it = images.find(textureId);

	if((it != images.end()) || (polyId >= polys.size()))
	{
		p = polys[polyId];
		image = &(it->second);

		if(!usingVBO)
		{
			tempX = -(((float)image->width * width) - (float)image->width);
			tempX *= 0.5f;
			tempX += (float)x;

			tempY = -(((float)image->height * height) - (float)image->height);
			tempY *= 0.5f;
			tempY += (float)y;

			glActiveTextureARB(GL_TEXTURE0_ARB);
			glEnable(GL_TEXTURE_2D);
			glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
			glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE);
			glBindTexture(GL_TEXTURE_2D, image->texture);
			glUniform1iARB(tex1Loc, 0);

			if(secondImage)
			{
				Image im;

				it2 = images.find(secondImage);
				if(it2 != images.end())
				{
					im = it2->second;

					glActiveTextureARB(GL_TEXTURE1_ARB);
					glEnable(GL_TEXTURE_2D);
					glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
					glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
					glBindTexture(GL_TEXTURE_2D, im.texture);
					glUniform1iARB(tex2Loc, 1);
				}
			}

			glMatrixMode(GL_MODELVIEW);
			glPushMatrix();

			glTranslated(x, y, 0);
			glScalef(width, height, 1.0f);
			glColor4f(r, g, b, a);
			glBegin(GL_TRIANGLES);
			{
				for(i=1;i<p.numPoints;++i)
				{
					glMultiTexCoord2fARB(GL_TEXTURE0_ARB, p.xTex[0], p.yTex[0]);
					glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.5f, 1.0f);
					glVertex2f(p.xPoints[0], p.yPoints[0]);

					glMultiTexCoord2fARB(GL_TEXTURE0_ARB, p.xTex[i], p.yTex[i]);
					glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 0.0f);
					glVertex2f(p.xPoints[i], p.yPoints[i]);

					if((i+1) < p.numPoints)
					{
						glMultiTexCoord2fARB(GL_TEXTURE0_ARB, p.xTex[i+1], p.yTex[i+1]);
						glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 0.0f);
						glVertex2f(p.xPoints[i+1], p.yPoints[i+1]);
					}
					else
					{
						glMultiTexCoord2fARB(GL_TEXTURE0_ARB, p.xTex[1], p.yTex[1]);
						glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 0.0f);
						glVertex2f(p.xPoints[1], p.yPoints[1]);
					}
				}
			}
			glEnd();
			glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

			glPopMatrix();

			glActiveTextureARB(GL_TEXTURE1_ARB);
			glDisable(GL_TEXTURE_2D);
			glActiveTextureARB(GL_TEXTURE0_ARB);
			glDisable(GL_TEXTURE_2D);
		}
	}
#ifdef DEBUG
	else
	{
		cout << "OpenGLDrawer::drawPoly(): Could not find image with id: ";
		cout << (unsigned int)textureId << endl;
	}
#endif
}

//------------------------------------------------------------------------------
void OpenGLDrawer::drawText(const string& text,
						    int16_t x,
						    int16_t y,
						    float height,
							float r,
							float g,
							float b,
							float a)
{
	int i;
	int tex;
	float xCount = x;
	float yCount = y;

	for(i=0;i<text.size();++i)
	{
		tex = getTexture(text[i]);

		if(tex != NumGlyphs)
		{
			glBindTexture(GL_TEXTURE_2D, glyphs[tex].texture);

			glColor4f(r, g, b, a);
			glBegin(GL_QUADS);
			{
				glTexCoord2f(0.0f, 0.0f);
				glVertex2f(xCount, yCount);

				glTexCoord2f(1.0f, 0.0f);
				glVertex2f(xCount+height, yCount);

				glTexCoord2f(1.0f, 1.0f);
				glVertex2f(xCount+height, yCount+height);

				glTexCoord2f(0.0f, 1.0f);
				glVertex2f(xCount, yCount+height);
			}
			glEnd();
			glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

			glBindTexture(GL_TEXTURE_2D, 0);
		}

		xCount += height;
	}
}

//------------------------------------------------------------------------------
void OpenGLDrawer::drawLine(int16_t x1,
						    int16_t y1,
						    int16_t x2,
						    int16_t y2,
						    float r,
						    float g,
						    float b,
						    float a)
{
	float x[4];
	float y[4];
	map<uint32_t, Image>::iterator it;
	const float dist = sqrtf(((x2-x1)*(x2-x1)) + ((y2-y1)*(y2-y1)));
	const float cX = (-(y2-y1)/dist)*4.0f;
	const float cY = ((x2-x1)/dist)*4.0f;

	//Bottom left point.
	x[0] = x1 + cX;
	y[0] = y1 + cY;
	//Bottom right point.
	x[1] = x2 + cX;
	y[1] = y2 + cY;
	//Top right point.
	x[2] = x2 - cX;
	y[2] = y2 - cY;
	//Top left point.
	x[3] = x1 - cX;
	y[3] = y1 - cY;

	it = images.find(lineImage);

	glBindTexture(GL_TEXTURE_2D, it->second.texture);
	glColor4f(r, g, b, a);
	glBegin(GL_QUADS);
	{
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 0.0f);
		glVertex2f(x[0], y[0]);

		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 1.0f);
		glVertex2f(x[1], y[1]);

		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 1.0f);
		glVertex2f(x[2], y[2]);

		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 0.0f);
		glVertex2f(x[3], y[3]);
	}
	glEnd();

	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	glBindTexture(GL_TEXTURE_2D, 0);
}

//------------------------------------------------------------------------------
void OpenGLDrawer::fillRect(int16_t x,
							int16_t y,
							int16_t w,
							int16_t h,
							uint8_t r,
							uint8_t g,
							uint8_t b,
							uint8_t a)
{
	//Just in case.
	glBindTexture(GL_TEXTURE_2D, 0);

	glColor4ub(r, g, b, a);
	glBegin(GL_QUADS);
	{
		glVertex2i(x, y);
		glVertex2i(x+w, y);
		glVertex2i(x+w, y+h);
		glVertex2i(x, y+h);
	}
	glEnd();
}

//------------------------------------------------------------------------------
void OpenGLDrawer::fillGradient(int16_t x,
							    int16_t y,
							    int16_t w,
							    int16_t h,
							    uint8_t r1,
							    uint8_t g1,
							    uint8_t b1,
							    uint8_t a1,
							    uint8_t r2,
							    uint8_t g2,
							    uint8_t b2,
							    uint8_t a2)
{
	glColor4ub(r1, g1, b1, a1);
	glBegin(GL_QUADS);
	{
		glVertex2i(x, y);
		glVertex2i(x+w, y);
		glColor4ub(r2, g2, b2, a2);
		glVertex2i(x+w, y+h);
		glVertex2i(x, y+h);
	}
	glEnd();
}

//------------------------------------------------------------------------------
void OpenGLDrawer::setViewport(int16_t x, int16_t y, int16_t w, int16_t h)
{
	int16_t x2, y2, w2, h2;

	//Convert input coords to pixels.
	x2 = (int16_t)(((float)x/1024.0f) * viewW);
	y2 = (int16_t)(((float)y/768.0f) * viewH);
	w2 = (int16_t)(((float)w/1024.0f) * viewW);
	h2 = (int16_t)(((float)h/768.0f) * viewH);

	//Set the viewport.
	glViewport(viewX+x2, viewY+(screenHeight-(y2+h2)), w2, h2);

	//Now setup the projection matrix.
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	//So our co-ordinates for drawing are 0-w (x) and h-0 (y).
	gluOrtho2D(0.0f, (float)w, (float)h, 0.0f);

	glMatrixMode(GL_MODELVIEW);
}

//------------------------------------------------------------------------------
void OpenGLDrawer::resetViewport()
{
	glViewport((int)viewX, (int)viewY, (int)viewW, (int)viewH);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	//So our co-ordinates for drawing are 0-1024 (x) and 768-0 (y).
	gluOrtho2D(0.0f, 1024.0f, 768.0f, 0.0f);

	glMatrixMode(GL_MODELVIEW);
}

//------------------------------------------------------------------------------
bool OpenGLDrawer::is16Bit()
{
	bool retval = false;
	const SDL_VideoInfo *info = SDL_GetVideoInfo();

	if(info->vfmt->BitsPerPixel == 16)
		retval = true;

	return retval;
}

//------------------------------------------------------------------------------
void OpenGLDrawer::initFonts()
{
	int i;
	Image tempIm;
	uint8_t data[16*16*4];

	//Fill data.
	for(i=0;i<(16*16*4);++i)
	{
		data[i] = 255;
	}

	tempIm.width = 16;
	tempIm.height = 16;
	tempIm.texWidth = 1.0f;
	tempIm.texHeight = 1.0f;

	//Fill glyphs.
	for(i=0;i<NumGlyphs;++i)
	{
		glGenTextures(1, &(tempIm.texture));

		glBindTexture(GL_TEXTURE_2D, tempIm.texture);

		glTexImage2D(GL_TEXTURE_2D,
					 0,
					 GL_RGBA,
					 16,
					 16,
					 0,
					 GL_RGBA,
					 GL_UNSIGNED_BYTE,
					 data);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

		glBindTexture(GL_TEXTURE_2D, 0);

		images[i] = tempIm;
	}
}

//------------------------------------------------------------------------------
bool OpenGLDrawer::isPowerOfTwo(uint32_t val)
{
	uint32_t tempint = 1;
	bool retval = false;

	//Surely there's a better way to do this?
	while(tempint < val)
		tempint *= 2;

	if(tempint == val)
		retval = true;

	return retval;
}

//------------------------------------------------------------------------------
uint32_t OpenGLDrawer::powerOfTwo(uint32_t val)
{
	uint32_t retval = 1;

	//Surely there's a better way to do this?
	while(retval < val)
		retval *= 2;

	return retval;
}

//------------------------------------------------------------------------------
string OpenGLDrawer::loadShaderFile(const string& path)
{
	string retval;

	if(path.length() > 0)
	{
		ifstream phil(path.c_str());

		if(phil.is_open())
		{
			uint32_t length;
			char *buffer;

			phil.seekg(0, ios::end);
			length = phil.tellg();
			phil.seekg(0, ios::beg);

			buffer = new char[length+1];

			phil.read(buffer, length);
			if(phil.gcount() < (length+1))
				buffer[phil.gcount()] = '\0';
			else
				buffer[length] = '\0';
			cout << buffer << endl;

			retval = buffer;

			delete [] buffer;
		//	phil >> retval.rdbuf();
		}
	}

	return retval;
}

//------------------------------------------------------------------------------
void OpenGLDrawer::printInfoLog(GLhandleARB obj)
{
    GLint infologLength = 0;
    GLsizei charsWritten  = 0;
    char *infoLog;
	
    glGetObjectParameterivARB(obj,
							  GL_OBJECT_INFO_LOG_LENGTH_ARB,
							  &infologLength);
	
    if(infologLength > 0)
    {
		infoLog = new char[infologLength];
		glGetInfoLogARB(obj, infologLength, &charsWritten, infoLog);
		cout << infoLog << endl;
		delete [] infoLog;
    }
}

//------------------------------------------------------------------------------
static const unsigned char temp1[] = {137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,8,0,0,0,8,8,6,0,0,0,196,15,190,139,0,0,0,7,116,73,77,
  69,7,217,11,5,18,24,55,156,239,108,44,0,0,0,9,112,72,89,115,0,0,13,172,0,0,13,172,1,239,6,198,71,0,0,0,4,103,65,77,
  65,0,0,177,143,11,252,97,5,0,0,0,31,73,68,65,84,120,218,99,248,255,255,63,19,16,43,2,241,129,255,8,112,0,42,198,196,196,64,0,
  12,15,5,0,208,222,27,187,199,235,156,253,0,0,0,0,73,69,78,68,174,66,96,130,0,0};
const char* OpenGLDrawer::line_png = (const char*)temp1;
