//	GLESDrawer.cpp - OpenGL ES 2.0 mode drawer.
//  ----------------------------------------------------------------------------
//	This file is part of 'NiallsAVLib', base code for any kind of audiovisual
//	apps.
//	Copyright (C) 2012  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 "GLESDrawer.h"
#include "FreetypeFontAtlas.h"
#include "DebugHeaders.h"
#include "ImageLoader.h"
#include "MiscHelper.h"
#include "UTF8File.h"

#include <cassert>

using std::wstringstream;
using std::stringstream;
using std::make_pair;
using std::wstring;
using std::string;
using std::vector;
using std::map;

//------------------------------------------------------------------------------
GLESDrawer::GLESDrawer():
drawingSize(1280.0f, 720.0f),
windowSize(1280.0f, 720.0f),
lastTexture(0),
lastVAO(0),
fontAtlasCreator(FreetypeFontAtlas::creator),
texInterp(true),
clearBetweenFrames(true),
line_pngSize(144)
{
	ShaderInternal tempShader;

	//This is to avoid a shaders[L""] being created with junk data.
	tempShader.shader = 0;
	tempShader.projectionLoc = -1;
	tempShader.matrixLoc = -1;
	tempShader.colourLoc = -1;
	tempShader.baseTextureLoc = -1;
	tempShader.textureExistsLoc = -1;
	tempShader.useVertexColourLoc = -1;
	shaders.insert(make_pair(L"", tempShader));
}

//------------------------------------------------------------------------------
GLESDrawer::~GLESDrawer()
{

}

//------------------------------------------------------------------------------
void GLESDrawer::initialise(int screenWidth,
								   int screenHeight,
								   const AppSettings& appSettings)
{
	float windowAspect;
	float drawingAspect;
	AppSettings::AspectRatioType ratioType = appSettings.getDrawingAreaAspect();

	//Set the sizes of the drawing area and window.
	windowSize.x = (float)screenWidth;
	windowSize.y = (float)screenHeight;
	drawingSize.x = (float)appSettings.getDrawingAreaWidth();
	drawingSize.y = (float)appSettings.getDrawingAreaHeight();
	windowAspect = windowSize.y/windowSize.x;
	drawingAspect = drawingSize.y/drawingSize.x;
	
	//If we're in an AlterRatio setup, we may need to update drawingSize
	//accordingly.
	if(ratioType == AppSettings::AlterRatio)
	{
		if(windowAspect != drawingAspect)
		{
			//Stretch/squash the drawing area along the y-axis.
			drawingSize.y = drawingSize.x * windowAspect;
		}
	}

	DebugStatement(L"GLESDrawer: Window dimensions: " << windowSize.x << L"x" << windowSize.y);
	DebugStatement(L"GLESDrawer: Drawing area dimensions: " << drawingSize.x << L"x" << drawingSize.y);

	//Initialise GLEW.
#ifndef OSX
	GLenum err;

	if(!glewAlready)
	{
		err = glewInit();
		if(err != GLEW_OK)
		{
			wstring tempstr;

			UTF8File::charToWstring(string((const char *)glewGetErrorString(err)), tempstr);
			DebugStatement(L"GLESDrawer: Could not initialise GLEW: " << tempstr.c_str());
		}
		else
		{
			wstring tempstr;

			UTF8File::charToWstring(string((const char *)glewGetString(GLEW_VERSION)), tempstr);
			DebugStatement(L"GLESDrawer: GLEW initalised. Using version: " << tempstr);
			glewAlready = true;
		}
	}
#else
	DebugStatement(L"GLESDrawer: OSX, Not using GLEW.");
#endif

	//Print out OpenGL system details.
	DebugStatement(L"GLESDrawer: OpenGL implementation = " << (char *)glGetString(GL_VENDOR) << L" : " << (char *)glGetString(GL_RENDERER) << L" : " << (char *)glGetString(GL_VERSION));

	//Check the maximum texture size on this system.
	{
		GLint tempint;
		glGetIntegerv(GL_MAX_TEXTURE_SIZE, &tempint);
		DebugStatement("GLESDrawer: GL_MAX_TEXTURE_SIZE = " << tempint);

		/*glTexImage2D(GL_PROXY_TEXTURE_2D,
					 0,
					 GL_RGBA,
					 2048,
					 2048,
					 0,
					 GL_RGBA,
					 GL_UNSIGNED_BYTE,
					 NULL);
		glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &tempint);
		DebugStatement("GL_TEXTURE_WIDTH = " << tempint);*/
	}

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

	//Clear to black (so that if we're widescreen and using
	//AppSettings::KeepRatio we'll have black bars at the sides).
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT);

	//Setup the viewport to draw into.
	if(ratioType == AppSettings::KeepRatio)
	{
		//In KeepRatio mode, we setup another viewport so the desired aspect
		//ratio is retained, w/black bars to ensure it's retained.
		if(windowAspect != drawingAspect)
		{
			if(windowAspect < drawingAspect)
			{
				viewportSize.x = (drawingSize.x/drawingSize.y) * windowSize.y;
				viewportSize.y = windowSize.y;
				viewportPos.x = (windowSize.x-viewportSize.x)*0.5f;
				viewportPos.y = 0;
			}
			else if(windowAspect > drawingAspect)
			{
				viewportSize.x = windowSize.x;
				viewportSize.y = windowSize.x * drawingAspect;
				viewportPos.x = 0.0f;
				viewportPos.y = (windowSize.y-viewportSize.y)*0.5f;
			}

			DebugStatement(L"GLESDrawer: KeepRatio; setting viewport to: " << viewportPos.x << L"x" << viewportPos.y << L" : " << viewportSize.x << L"x" << viewportSize.y);
			glViewport((int)viewportPos.x,
					   (int)viewportPos.y,
					   (int)viewportSize.x,
					   (int)viewportSize.y);
		}
	}

	if(viewportSize.x <= 0.0f)
		viewportSize.x = windowSize.x;
	if(viewportSize.y <= 0.0f)
		viewportSize.y = windowSize.y;

	//Load the default shader.
	DebugStatement(L"GLESDrawer: Loading default shader.");
	if(loadShader(defaultVertexShader, defaultFragmentShader, L"-default-shader-"))
	{
		DebugStatement(L"GLESDrawer: Default shader loaded.");
		useShader(L"-default-shader-");
	}
	else
		DebugStatement(L"GLESDrawer: Could not load default shader. Will not be able to draw anything :(");

	//To write info to the debug log file (we can't really do anything if it
	//doesn't support VAOs).
	canSupport(VertexArrayObjects);
	
	//Setup the VBO used by fillRect().
	{
		GLuint vertexBuffer;
		GLfloat bufferData[] = {0.0f, 0.0f,
								0.0f, 1.0f,
								1.0f, 0.0f,
								1.0f, 1.0f};
		GLfloat colourData[] = {0.0f, 0.0f, 0.0f, 1.0f,
								0.0f, 0.0f, 0.0f, 1.0f,
								0.0f, 0.0f, 0.0f, 1.0f,
								0.0f, 0.0f, 0.0f, 1.0f};

		glGenBuffers(1, &vertexBuffer);
		glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
		glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*8, bufferData, GL_STATIC_DRAW);

		//Vertex colours will be updated with every fillGradient() call.
		glGenBuffers(1, &rectVBO);
		glBindBuffer(GL_ARRAY_BUFFER, rectVBO);
		glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*16, colourData, GL_STREAM_DRAW);

		glGenVertexArrays(1, &rectVAO);
		glBindVertexArray(rectVAO);
		
		glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
		glEnableVertexAttribArray(vertexPositionLoc);
		glVertexAttribPointer(vertexPositionLoc,
								2,
								GL_FLOAT,
								GL_FALSE,
								sizeof(GLfloat)*2,
								0);
		
		glBindBuffer(GL_ARRAY_BUFFER, rectVBO);
		glEnableVertexAttribArray(vertexColourLoc);
		glVertexAttribPointer(vertexColourLoc,
							  4,
							  GL_FLOAT,
							  GL_FALSE,
							  sizeof(GLfloat)*4,
							  0);

		glBindVertexArray(0);
		lastVAO = 0;

		DebugStatement(L"GLESDrawer: fillRect() VAO initialised.");
	}
	
	//Setup the VAO/VBO used by drawSubImage()/drawTiledImage().
	{
		GLuint vertexBuffer;
		GLfloat bufferData[] = {0.0f, 0.0f,
								0.0f, 1.0f,
								1.0f, 0.0f,
								1.0f, 1.0f};
		GLfloat texCoords[] = {0.0f, 0.0f,
							   0.0f, 1.0f,
							   1.0f, 0.0f,
							   1.0f, 1.0f};

		//Vertex positions don't change.
		glGenBuffers(1, &vertexBuffer);
		glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
		glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*8, bufferData, GL_STATIC_DRAW);

		//Texture coordinates will be updated with every draw call.
		glGenBuffers(1, &texCoordsVBO);
		glBindBuffer(GL_ARRAY_BUFFER, texCoordsVBO);
		glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*8, texCoords, GL_STREAM_DRAW);

		glGenVertexArrays(1, &texCoordsVAO);
		glBindVertexArray(texCoordsVAO);
		
		glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
		glEnableVertexAttribArray(vertexPositionLoc);
		glVertexAttribPointer(vertexPositionLoc,
								2,
								GL_FLOAT,
								GL_FALSE,
								sizeof(GLfloat)*2,
								0);
		
		glBindBuffer(GL_ARRAY_BUFFER, texCoordsVBO);
		glEnableVertexAttribArray(baseTexCoordsLoc);
		glVertexAttribPointer(baseTexCoordsLoc,
							  2,
							  GL_FLOAT,
							  GL_FALSE,
							  sizeof(GLfloat)*2,
							  0);

		glBindVertexArray(0);
		lastVAO = 0;

		DebugStatement(L"GLESDrawer: drawSubImage()/drawTiledImage() VAO initialised.");
	}

	//Setup the connected line stuff.
	{
		int i;
		vector<GLfloat> textureData;

		//Fill out connectedData.
		for(i=0;i<ConnectedDefaultSize;++i)
		{
			//Vertex 0.
			connectedData.push_back(0.0f);
			connectedData.push_back(0.0f);

			//Colour 0.
			connectedData.push_back(1.0f);
			connectedData.push_back(0.0f);
			connectedData.push_back(0.0f);
			connectedData.push_back(0.5f);

			//Vertex 1.
			connectedData.push_back(0.0f);
			connectedData.push_back(0.0f);

			//Colour 1.
			connectedData.push_back(1.0f);
			connectedData.push_back(0.0f);
			connectedData.push_back(0.0f);
			connectedData.push_back(0.5f);
		}

		//Fill out textureData.
		for(i=0;i<ConnectedDefaultSize/2;++i)
		{
			//Tex coord 0.
			textureData.push_back(0.0f);
			textureData.push_back(0.0f);

			//Tex coord 1.
			textureData.push_back(1.0f);
			textureData.push_back(0.0f);

			//Tex coord 2.
			textureData.push_back(0.0f);
			textureData.push_back(1.0f);

			//Tex coord 3.
			textureData.push_back(1.0f);
			textureData.push_back(1.0f);
		}

		//Setup the texture VBO.
		glGenBuffers(1, &connectedTexVBO);
		glBindBuffer(GL_ARRAY_BUFFER, connectedTexVBO);
		glBufferData(GL_ARRAY_BUFFER,
					 sizeof(GLfloat)*textureData.size(),
					 &(textureData[0]),
					 GL_STATIC_DRAW);

		//Now setup the vertex and colour VBO.
		glGenBuffers(1, &connectedVBO);
		glBindBuffer(GL_ARRAY_BUFFER, connectedVBO);
		glBufferData(GL_ARRAY_BUFFER,
					 sizeof(GLfloat)*connectedData.size(),
					 &(connectedData[0]),
					 GL_STREAM_DRAW);

		//Setup the VAO.
		glGenVertexArrays(1, &connectedVAO);
		glBindVertexArray(connectedVAO);
		
		glBindBuffer(GL_ARRAY_BUFFER, connectedTexVBO);
		glEnableVertexAttribArray(baseTexCoordsLoc);
		glVertexAttribPointer(baseTexCoordsLoc,
							  2,
							  GL_FLOAT,
							  GL_FALSE,
							  sizeof(GLfloat)*2,
							  0);
		
		glBindBuffer(GL_ARRAY_BUFFER, connectedVBO);
		glEnableVertexAttribArray(vertexPositionLoc);
		glVertexAttribPointer(vertexPositionLoc,
							  2,
							  GL_FLOAT,
							  GL_FALSE,
							  sizeof(GLfloat)*6,
							  0);
		glEnableVertexAttribArray(vertexColourLoc);
		glVertexAttribPointer(vertexColourLoc,
							  4,
							  GL_FLOAT,
							  GL_FALSE,
							  sizeof(GLfloat)*6,
							  (void *)(sizeof(GLfloat)*2));

		glBindVertexArray(0);
		lastVAO = 0;

		DebugStatement(L"GLESDrawer: Connected line VAO initialised.");
	}

	//Initialise frame buffer object.
	if(canSupport(FrameBuffer))
	{
		GLuint depthBuffer;
		ESImage tempIm;

		//Generate and bind the frame buffer object.
		glGenFramebuffers(1, &frameBufferObject);
		glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);
		
		//Generate and attach the frame buffer's depth buffer.
		glGenRenderbuffers(1, &depthBuffer);
		glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
		glRenderbufferStorage(GL_RENDERBUFFER,
							  GL_DEPTH_COMPONENT,
							  (int)drawingSize.x,
							  (int) drawingSize.y);
		glFramebufferRenderbuffer(GL_FRAMEBUFFER,
								  GL_DEPTH_ATTACHMENT,
								  GL_RENDERBUFFER,
								  depthBuffer);

		//Generate the frame buffer's texture.
		tempIm.size.x = drawingSize.x;
		tempIm.size.y = drawingSize.y;
		tempIm.sizeWithin.x = 1.0f;
		tempIm.sizeWithin.y = 1.0f;
		glGenTextures(1, &tempIm.texture);
		glBindTexture(GL_TEXTURE_2D, tempIm.texture);
		glTexImage2D(GL_TEXTURE_2D,
					 0,
					 GL_RGBA8,
					 (int)drawingSize.x,
					 (int)drawingSize.y,
					 0,
					 GL_RGBA,
					 GL_UNSIGNED_BYTE,
					 NULL);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

		//Attach the texture to the frame buffer.
		glFramebufferTexture2D(GL_FRAMEBUFFER,
							   GL_COLOR_ATTACHMENT0,
							   GL_TEXTURE_2D,
							   tempIm.texture,
							   0);

		//Setup the texture's associated vertex buffer object.
		{
			//						vertex							texture
			GLfloat bufferData[] = {0.0f, 0.0f,						0.0f, 0.0f,
									0.0f, tempIm.size.y,			0.0f, tempIm.sizeWithin.y,
									tempIm.size.x, 0.0f,			tempIm.sizeWithin.x, 0.0f,
									tempIm.size.x, tempIm.size.y,	tempIm.sizeWithin.x, tempIm.sizeWithin.y};

			glGenBuffers(1, &tempIm.vertexBuffer);
			glBindBuffer(GL_ARRAY_BUFFER, tempIm.vertexBuffer);
			glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*16, bufferData, GL_STATIC_DRAW);

			glGenVertexArrays(1, &tempIm.vertexArray);
			glBindVertexArray(tempIm.vertexArray);
			
			glBindBuffer(GL_ARRAY_BUFFER, tempIm.vertexBuffer);
			glEnableVertexAttribArray(vertexPositionLoc);
			glEnableVertexAttribArray(baseTexCoordsLoc);
			glVertexAttribPointer(vertexPositionLoc,
								  2,
								  GL_FLOAT,
								  GL_FALSE,
								  sizeof(GLfloat)*4,
								  0);
			glVertexAttribPointer(baseTexCoordsLoc,
								  2,
								  GL_FLOAT,
								  GL_FALSE,
								  sizeof(GLfloat)*4,
								  (void *)(sizeof(GLfloat)*2));

			glBindVertexArray(0);

			tempIm.arrayOffset = 0;
		}

		//Add it to our images map.
		images.insert(make_pair(L"-frame-buffer-image-", tempIm));

		GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
		if(status != GL_FRAMEBUFFER_COMPLETE)
		{
			DebugStatement(L"GLESDrawer: Could not create frame buffer.");
		}
		else
			DebugStatement(L"GLESDrawer: Frame buffer initialised.");
		
		//Unbind stuff.
		glBindTexture(GL_TEXTURE_2D, 0);
		glBindFramebuffer(GL_FRAMEBUFFER, 0);
	}

	//Now set up everything else.

	//So our co-ordinates for drawing are 0-drawingAreaWidth (x)
	//and drawingAreaHeight-0 (y).
	projectionMatrix.generateOrthoProjection(0.0f,
											 drawingSize.x,
											 drawingSize.y,
											 0.0f);
	glUniformMatrix4fv(shaders[currentShader].projectionLoc,
					   1,
					   GL_FALSE,
					   projectionMatrix.getMatrix());

	//glShadeModel(GL_SMOOTH);
	//glClearDepthf(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);
	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);

	//Load the line image.
	if(images.find(L"-default-line-") == images.end())
	{
		ImageLoader imLoad;
		ImageData data;
		ESImage tempIm;

		data = imLoad.loadImage(line_png, line_pngSize);
		loadSingleImage(data.data, data.width, data.height, L"-default-line-");
	}
}

//------------------------------------------------------------------------------
void GLESDrawer::setFontRenderer(FontAtlas::Creator& renderer)
{
	fontAtlasCreator = renderer;
}

//------------------------------------------------------------------------------
void GLESDrawer::loadSingleImage(uint8_t *data,
								 int width,
								 int height,
								 const wstring& name)
{
	ESImage tempIm;
	int texWidth, texHeight;

	DebugStatement(L"GLESDrawer: Loading single image: " << name);

	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];

			DebugStatement(L"GLESDrawer: Image is not power-of-two. Making it so.");

			//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<(unsigned)height;++y)
			{
				for(x=0;x<((unsigned)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*height*4) - (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;
				if(i < (((unsigned)width*4)-1))
					++i;
			}

			//Inform tempIm of the dimensions.
			tempIm.size.x = (float)width;
			tempIm.size.y = (float)height;
			tempIm.sizeWithin.x = (float)width/(float)w;
			tempIm.sizeWithin.y = (float)height/(float)h;

			texWidth = w;
			texHeight = h;

			DebugStatement(L"GLESDrawer: Updated image dimensions: " << width << "x" << height << "(image) : " << w << "x" << h << L"(texture)");

			//Delete the old data.
			delete [] data;
			//Make sure data now points to the new data.
			data = tempData;
		}
		else
		{
			tempIm.size.x = (float)width;
			tempIm.size.y = (float)height;
			tempIm.sizeWithin.x = 1.0f;
			tempIm.sizeWithin.y = 1.0f;

			texWidth = width;
			texHeight = height;
		}

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

		glBindTexture(GL_TEXTURE_2D, tempIm.texture);

		glTexImage2D(GL_TEXTURE_2D,
					 0,
					 GL_RGBA,
					 texWidth,
					 texHeight,
					 0,
					 GL_RGBA,
					 GL_UNSIGNED_BYTE,
					 data);

		if(texInterp)
		{
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		}
		else
		{
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		}
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

		glBindTexture(GL_TEXTURE_2D, 0);

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

		//Setup the associated vertex buffer object.
		{
			//						vertex							texture
			GLfloat bufferData[] = {0.0f, 0.0f,						0.0f, 0.0f,
									0.0f, tempIm.size.y,			0.0f, tempIm.sizeWithin.y,
									tempIm.size.x, 0.0f,			tempIm.sizeWithin.x, 0.0f,
									tempIm.size.x, tempIm.size.y,	tempIm.sizeWithin.x, tempIm.sizeWithin.y};

			glGenBuffers(1, &tempIm.vertexBuffer);
			glBindBuffer(GL_ARRAY_BUFFER, tempIm.vertexBuffer);
			glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*16, bufferData, GL_STATIC_DRAW);

			glGenVertexArrays(1, &tempIm.vertexArray);
			glBindVertexArray(tempIm.vertexArray);
			
			glBindBuffer(GL_ARRAY_BUFFER, tempIm.vertexBuffer);
			glEnableVertexAttribArray(vertexPositionLoc);
			glEnableVertexAttribArray(baseTexCoordsLoc);
			glVertexAttribPointer(vertexPositionLoc,
								  2,
								  GL_FLOAT,
								  GL_FALSE,
								  sizeof(GLfloat)*4,
								  0);
			glVertexAttribPointer(baseTexCoordsLoc,
								  2,
								  GL_FLOAT,
								  GL_FALSE,
								  sizeof(GLfloat)*4,
								  (void *)(sizeof(GLfloat)*2));

			glBindVertexArray(0);
			lastVAO = 0;

			tempIm.arrayOffset = 0;
		}

		//Add tempIm to our list of images.
		images.insert(make_pair(name, tempIm));

		DebugStatement(L"GLESDrawer: Single image " << name << L" loaded.");
	}
	else
		DebugStatement(L"GLESDrawer: Could not load image " << name << L" No data passed in!");
}

//------------------------------------------------------------------------------
void GLESDrawer::loadTextureAtlas(uint8_t *data,
								  int width,
								  int height,
								  const TextureAtlas& atlas)
{
	ESImage tempTexture;

	assert(atlas.getNumImages() > 0);

	DebugStatement(L"GLESDrawer: Loading texture atlas: " << atlas.getImage(0).atlasName);
	DebugStatement(L"GLESDrawer: Atlas num images: " << atlas.getNumImages());
	DebugStatement(L"GLESDrawer: Atlas dimensions: " << width << L"x" << height);

	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];

			DebugStatement(L"GLESDrawer: Image is not power-of-two. Making it so.");

			//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<(unsigned)height;++y)
			{
				for(x=0;x<((unsigned)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*height*4) - (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;
				if(i < (((unsigned)width*4)-1))
					++i;
			}

			//Inform tempIm of the dimensions.
			tempTexture.size.x = (float)w;
			tempTexture.size.y = (float)h;
			tempTexture.sizeWithin.x = (float)width/(float)w;
			tempTexture.sizeWithin.y = (float)height/(float)h;

			DebugStatement(L"GLESDrawer: Updated image dimensions: " << width << "x" << height << "(image) : " << w << "x" << h << L"(texture)");

			//Delete the old data.
			delete [] data;
			//Make sure data now points to the new data.
			data = tempData;
		}
		else
		{
			tempTexture.size.x = (float)width;
			tempTexture.size.y = (float)height;
			tempTexture.sizeWithin.x = 1.0f;
			tempTexture.sizeWithin.y = 1.0f;
		}

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

		glBindTexture(GL_TEXTURE_2D, tempTexture.texture);

		glTexImage2D(GL_TEXTURE_2D,
					 0,
					 GL_RGBA,
					 (int)tempTexture.size.x,
					 (int)tempTexture.size.y,
					 0,
					 GL_RGBA,
					 GL_UNSIGNED_BYTE,
					 data);

		if(texInterp)
		{
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		}
		else
		{
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		}
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(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 all the images in the atlas to images.
		{
			int i;
			GLuint vertexArray;
			GLuint vertexBuffer;
			vector<GLfloat> bufferData;

			//Create the VAO.
			//(texture atlases use a single VAO to minimise the number of bind calls
			// needed)
			glGenVertexArrays(1, &vertexArray);

			glGenBuffers(1, &vertexBuffer);
			glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);

			for(i=0;i<atlas.getNumImages();++i)
			{
				ESImage tempIm;
				const AtlasImage& atlasIm = atlas.getImage(i);

				tempIm.texture = tempTexture.texture;
				//Atlas image size in pixels.
				tempIm.size.x = atlasIm.size.x * tempTexture.sizeWithin.x * tempTexture.size.x;
				tempIm.size.y = atlasIm.size.y * tempTexture.sizeWithin.y * tempTexture.size.y;
				//Atlas image position & size relative to the texture.
				tempIm.posWithin.x = atlasIm.position.x * tempTexture.sizeWithin.x;
				tempIm.posWithin.y = atlasIm.position.y * tempTexture.sizeWithin.y;
				tempIm.sizeWithin.x = atlasIm.size.x * tempTexture.sizeWithin.x;
				tempIm.sizeWithin.y = atlasIm.size.y * tempTexture.sizeWithin.y;

				tempIm.vertexArray = vertexArray;
				tempIm.vertexBuffer = vertexBuffer;
				tempIm.arrayOffset = bufferData.size()/4;

				//Fill out the vertex data (position then tex coords).
				bufferData.push_back(0.0f);
				bufferData.push_back(0.0f);
				bufferData.push_back(tempIm.posWithin.x);
				bufferData.push_back(tempIm.posWithin.y);

				bufferData.push_back(0.0f);
				bufferData.push_back(tempIm.size.y);
				bufferData.push_back(tempIm.posWithin.x);
				bufferData.push_back(tempIm.posWithin.y + tempIm.sizeWithin.y);

				bufferData.push_back(tempIm.size.x);
				bufferData.push_back(0.0f);
				bufferData.push_back(tempIm.posWithin.x + tempIm.sizeWithin.x);
				bufferData.push_back(tempIm.posWithin.y);

				bufferData.push_back(tempIm.size.x);
				bufferData.push_back(tempIm.size.y);
				bufferData.push_back(tempIm.posWithin.x + tempIm.sizeWithin.x);
				bufferData.push_back(tempIm.posWithin.y + tempIm.sizeWithin.y);

				images.insert(make_pair(atlasIm.name, tempIm));
			}

			//Load the data into the vertex array.
			glBufferData(GL_ARRAY_BUFFER,
							sizeof(GLfloat)*bufferData.size(),
							&(bufferData[0]), GL_STATIC_DRAW);

			glBindVertexArray(vertexArray);
			
			glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
			glEnableVertexAttribArray(vertexPositionLoc);
			glEnableVertexAttribArray(baseTexCoordsLoc);
			glVertexAttribPointer(vertexPositionLoc,
									2,
									GL_FLOAT,
									GL_FALSE,
									sizeof(GLfloat)*4,
									0);
			glVertexAttribPointer(baseTexCoordsLoc,
									2,
									GL_FLOAT,
									GL_FALSE,
									sizeof(GLfloat)*4,
									(void *)(sizeof(GLfloat)*2));

			glBindVertexArray(0);
			lastVAO = 0;
		}

		DebugStatement(L"GLESDrawer: TextureAtlas image " << atlas.getImage(0).atlasName << L" loaded.");
	}
	else
		DebugStatement(L"GLESDrawer: Could not load image << " << atlas.getImage(0).atlasName << L" No data passed in!");
}

//------------------------------------------------------------------------------
void GLESDrawer::releaseImage(const wstring& name)
{
	GLuint texture;
	GLuint vertexArray;
	GLuint bufferObject;
	map<wstring, ESImage>::iterator it = images.find(name);

	DebugStatement(L"GLESDrawer: Releasing image " << name);

	if(it != images.end())
	{
		//Delete the texture.
		texture = it->second.texture;
		glDeleteTextures(1, &texture);

		//Delete the VBO and VAO.
		bufferObject = it->second.vertexBuffer;
		vertexArray = it->second.vertexArray;
		glDeleteBuffers(1, &bufferObject);
		glDeleteVertexArrays(1, &vertexArray);

		images.erase(it);

		//Now check whether we have any other images registered which used the
		//same texture.
		for(it=images.begin();it!=images.end();)
		{
			map<wstring, ESImage>::iterator it2 = it;

			++it;

			if(it2->second.texture == texture)
			{
				//Delete this image's VBO and VAO.
				bufferObject = it2->second.vertexBuffer;
				vertexArray = it2->second.vertexArray;
				glDeleteBuffers(1, &bufferObject);
				glDeleteVertexArrays(1, &vertexArray);

				images.erase(it2);
			}
		}
		DebugStatement(L"GLESDrawer: Image " << name << L" released.");
	}
	else
		DebugStatement(L"GLESDrawer: Could not find image " << name << " to release.");
}

//------------------------------------------------------------------------------
bool GLESDrawer::loadShader(const wstring& vertex,
							const wstring& fragment,
							const wstring& name)
{
	bool retval = false;

	DebugStatement(L"GLESDrawer: Loading shader " << name);

	if(canSupport(Shaders))
	{
		GLuint vertexObject, fragmentObject;
		string vertexText, fragmentText;
		GLuint shaderProgram;
		GLint err;
		
		UTF8File::wstringToChar(vertex, vertexText);
		UTF8File::wstringToChar(fragment, fragmentText);

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

		//Read the shaders from their files.
		const char *vertexChars = vertexText.c_str();
		const char *fragmentChars = fragmentText.c_str();

		//Read the shader source into the shader objects.
		glShaderSource(vertexObject, 1, &vertexChars, NULL);
		glShaderSource(fragmentObject, 1, &fragmentChars, NULL);

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

		//Test they both compiled okay.
		glGetShaderiv(vertexObject, GL_COMPILE_STATUS, &err);
		if(err == GL_FALSE)
			DebugStatement(L"GLESDrawer: loadShader(); Could not compile Vertex shader " << name);
		glGetShaderiv(fragmentObject, GL_COMPILE_STATUS, &err);
		if(err == GL_FALSE)
			DebugStatement(L"GLESDrawer: loadShader(); Could not compile Fragment shader " << name);

		//Create the program object.
		shaderProgram = glCreateProgram();

		if(shaderProgram)
		{
			ShaderInternal tempShader;

			//Attach the shaders to the program.
			glAttachShader(shaderProgram, vertexObject);
			glAttachShader(shaderProgram, fragmentObject);

			vertexPositionLoc = 0;
			glBindAttribLocation(shaderProgram, vertexPositionLoc, "avlVertexPosition");
			baseTexCoordsLoc = 1;
			glBindAttribLocation(shaderProgram, baseTexCoordsLoc, "avlBaseTexCoords");
			vertexColourLoc = 2;
			glBindAttribLocation(shaderProgram, vertexColourLoc, "avlVertexColour");

			//Finally, link the program.
			glLinkProgram(shaderProgram);

			//Test the linking went okay.
			glGetProgramiv(shaderProgram, GL_LINK_STATUS, &err);
			if(err == GL_FALSE)
				DebugStatement(L"GLESDrawer: loadShader(); Could not link shader " << name);

			glUseProgram(shaderProgram);

			//Setup the shader's various uniforms.
			tempShader.projectionLoc = glGetUniformLocation(shaderProgram, "avlProjectionMatrix");
			tempShader.matrixLoc = glGetUniformLocation(shaderProgram, "avlModelviewMatrix");

			tempShader.colourLoc = glGetUniformLocation(shaderProgram, "avlColour");
			tempShader.baseTextureLoc = glGetUniformLocation(shaderProgram, "avlBaseTexture");
			tempShader.textureExistsLoc = glGetUniformLocation(shaderProgram, "avlTextureExists");
			tempShader.useVertexColourLoc = glGetUniformLocation(shaderProgram, "avlUseVertexColour");

			//Output the info log.
			DebugStatement(L"Vertex shader infolog for " << name);
			printInfoLog(vertexObject, false);
			DebugStatement(L"Fragment shader infolog for " << name);
			printInfoLog(fragmentObject, false);
			DebugStatement(L"Shader program infolog for " << name);
			printInfoLog(shaderProgram, true);

			tempShader.shader = shaderProgram;

			//Add shaderProgram to our shaders map.
			shaders.insert(make_pair(name, tempShader));

			retval = true;
			DebugStatement(L"GLESDrawer: Shader " << name << L" loaded.");
			
			//We need this to initialise the shader's matrices.
			resetViewport();
			if(currentShader != L"")
				glUseProgram(shaders[currentShader].shader);
		}
		else
			DebugStatement(L"GLESDrawer: Could not create shader program for shader " << name);
	}
	else
		DebugStatement(L"GLESDrawer: System does not support shaders. Shader " << name << L" not loaded.");

	return retval;
}

//------------------------------------------------------------------------------
void GLESDrawer::registerShaderParam(const std::wstring& name)
{
	string narrowName;

	UTF8File::wstringToChar(name, narrowName);
	shaders[currentShader].uniformLocs[name] = glGetUniformLocation(shaders[currentShader].shader,
																	narrowName.c_str());
}

//------------------------------------------------------------------------------
void GLESDrawer::releaseShader(const wstring& name)
{
	map<wstring, ShaderInternal>::iterator it;

	DebugStatement(L"GLESDrawer: Releasing shader " << name);

	it = shaders.find(name);
	if(it != shaders.end())
	{
		glDeleteProgram(it->second.shader);
		shaders.erase(it);

		DebugStatement(L"GLESDrawer: Shader " << name << L" released.");
	}
	else
		DebugStatement(L"GLESDrawer: Could not find shader " << name << L" to release.");
}

//------------------------------------------------------------------------------
void GLESDrawer::loadFont(const wstring& name,
						  float size,
						  const wstring& identifier)
{
	FontAtlasPtr fontAtlas;
	FilePath fontPath = FontAtlas::findFontFile(name);

	DebugStatement(L"GLESDrawer: Loading font face " << name << L" to " << identifier);

	if(fontPath.exists())
	{
		ImageData data;
		ImageLoader imLoad;

		//Create the FontMetrics we'll use.
		fonts.insert(make_pair(identifier, FontInternal()));

		//Create the FontAtlas/TextureAtlas from the font file, fill out the
		//associated FontMetrics.
		fontAtlas = fontAtlasCreator.createInstance(fontPath, size, fonts[identifier].metrics, identifier);

		if(fonts[identifier].metrics.size() > 0)
		{
			//Load the image data from the FontAtlas.
			//data = imLoad.loadImage(fontAtlas->getImageData(), fontAtlas->getImageDataSize());
			data.data = (uint8_t *)(fontAtlas->getImageData());
			data.width = fontAtlas->getAtlasDimensions();
			data.height = fontAtlas->getAtlasDimensions();

			//Load the decoded image data onto the GPU, storing the individual
			//glyphs of the font as Images, identical to the Images we use in
			//drawImage() etc.
			loadTextureAtlas(data.data, data.width, data.height, *fontAtlas);

			//Get currentTexture from one of the glyphs we loaded.
			fonts[identifier].currentTexture = images[fontAtlas->getImage(0).name].texture;
			fonts[identifier].textures.push_back(fonts[identifier].currentTexture);

			fonts[identifier].atlas = fontAtlas;

			DebugStatement(L"GLESDrawer: Font " << identifier << L" loaded.");
		}
		else
		{
			DebugStatement(L"GLESDrawer: Could not load font file: " << fontPath.getPath().c_str());

			fonts.erase(fonts.find(identifier));
		}
	}
	else
		DebugStatement(L"GLESDrawer: Font file " << fontPath.getPath().c_str() << L" doesn't exist.");
}

//------------------------------------------------------------------------------
void GLESDrawer::releaseFont(const wstring& identifier)
{
	map<wstring, FontInternal>::iterator it;

	DebugStatement(L"GLESDrawer: Releasing font " << identifier);

	it = fonts.find(identifier);

	if(it != fonts.end())
	{
		//Check the fonts entry actually has some glyphs in it.
		assert(it->second.metrics.size() > 0);

		//First release the font texture.
		//(we do this by grabbing the image id from the first glyph; because all
		// the glyphs are rendered on the same texture, this will have the
		// effect of removing them all from the GPU and our images map)
		releaseImage(it->second.metrics.begin()->second.image);

		//Now erase the entry in fonts.
		fonts.erase(it);

		DebugStatement(L"GLESDrawer: Font " << identifier << L" released.");
	}
	else
		DebugStatement(L"GLESDrawer: Could not find font " << identifier << L" to release.");
}

//------------------------------------------------------------------------------
FontMetrics::iterator GLESDrawer::loadMissingGlyph(wchar_t missingCharacter,
												   FontMetrics& metrics,
												   const wstring& fontId)
{
	AtlasImage *atlasImage;
	unsigned char *imageData;
	int imageWidth, imageHeight;
	bool needNewAtlas;
	FontMetrics::iterator retval = metrics.end();
	FontAtlasPtr tempAt = fonts[fontId].atlas;

	DebugStatement(L"GLESDrawer: Loading missing glyph " << missingCharacter << L" to font " << fontId);

	fonts[fontId].atlas->getMissingGlyphData(missingCharacter,
											 metrics,
											 &atlasImage,
											 &imageData,
											 imageWidth,
											 imageHeight,
											 needNewAtlas);

	if(atlasImage)
	{
		ESImage tempIm;

		if(needNewAtlas)
		{
			DebugStatement(L"GLESDrawer: Need new atlas to accommodate missing glyph.");

			glGenTextures(1, &(fonts[fontId].currentTexture));
			fonts[fontId].textures.push_back(fonts[fontId].currentTexture);

			glBindTexture(GL_TEXTURE_2D, fonts[fontId].currentTexture);

			glTexImage2D(GL_TEXTURE_2D,
						 0,
						 GL_RGBA,
						 imageWidth,
						 imageHeight,
						 0,
						 GL_RGBA,
						 GL_UNSIGNED_BYTE,
						 imageData);
			if(texInterp)
			{
				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
			}
			else
			{
				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
			}
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

			glBindTexture(GL_TEXTURE_2D, 0);

			tempIm.texture = fonts[fontId].currentTexture;
			tempIm.size = atlasImage->size * (float)fonts[fontId].atlas->getAtlasDimensions();
			tempIm.posWithin = atlasImage->position;
			tempIm.sizeWithin = atlasImage->size;
			tempIm.arrayOffset = 0;

			//Setup the associated vertex buffer object.
			{
				//						vertex							texture
				GLfloat bufferData[] = {0.0f, 0.0f,						tempIm.posWithin.x, tempIm.posWithin.y,
										0.0f, tempIm.size.y,			tempIm.posWithin.x, tempIm.posWithin.y + tempIm.sizeWithin.y,
										tempIm.size.x, 0.0f,			tempIm.posWithin.x + tempIm.sizeWithin.x, tempIm.posWithin.y,
										tempIm.size.x, tempIm.size.y,	tempIm.posWithin.x + tempIm.sizeWithin.x, tempIm.posWithin.y + tempIm.sizeWithin.y};

				glGenBuffers(1, &tempIm.vertexBuffer);
				glBindBuffer(GL_ARRAY_BUFFER, tempIm.vertexBuffer);
				glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*16, bufferData, GL_DYNAMIC_DRAW);
				
				glGenVertexArrays(1, &tempIm.vertexArray);
				glBindVertexArray(tempIm.vertexArray);
				
				glBindBuffer(GL_ARRAY_BUFFER, tempIm.vertexBuffer);
				glEnableVertexAttribArray(vertexPositionLoc);
				glEnableVertexAttribArray(baseTexCoordsLoc);
				glVertexAttribPointer(vertexPositionLoc,
										2,
										GL_FLOAT,
										GL_FALSE,
										sizeof(GLfloat)*4,
										0);
				glVertexAttribPointer(baseTexCoordsLoc,
										2,
										GL_FLOAT,
										GL_FALSE,
										sizeof(GLfloat)*4,
										(void *)(sizeof(GLfloat)*2));

				glBindVertexArray(0);
				lastVAO = 0;
			}

			images.insert(make_pair(atlasImage->name, tempIm));

			delete [] imageData;

			DebugStatement(L"GLESDrawer: New atlas initialised.");
		}
		else
		{
			DebugStatement(L"GLESDrawer: Inserting glyph into existing atlas.");

			glBindTexture(GL_TEXTURE_2D, fonts[fontId].currentTexture);
			glTexSubImage2D(GL_TEXTURE_2D,
							0,
							(int)(atlasImage->position.x * fonts[fontId].atlas->getAtlasDimensions()),
							(int)(atlasImage->position.y * fonts[fontId].atlas->getAtlasDimensions()),
							imageWidth,
							imageHeight,
							GL_RGBA,
							GL_UNSIGNED_BYTE,
							imageData);
			if(texInterp)
			{
				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
			}
			else
			{
				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
			}
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

			glBindTexture(GL_TEXTURE_2D, 0);

			tempIm.texture = fonts[fontId].currentTexture;
			tempIm.size.x = (float)imageWidth;
			tempIm.size.y = (float)imageHeight;
			tempIm.posWithin = atlasImage->position;
			tempIm.sizeWithin = atlasImage->size;
			tempIm.arrayOffset = 0;

			//Setup the associated vertex buffer object.
			{
				//						vertex							texture
				GLfloat bufferData[] = {0.0f, 0.0f,						tempIm.posWithin.x, tempIm.posWithin.y,
										0.0f, tempIm.size.y,			tempIm.posWithin.x, tempIm.posWithin.y + tempIm.sizeWithin.y,
										tempIm.size.x, 0.0f,			tempIm.posWithin.x + tempIm.sizeWithin.x, tempIm.posWithin.y,
										tempIm.size.x, tempIm.size.y,	tempIm.posWithin.x + tempIm.sizeWithin.x, tempIm.posWithin.y + tempIm.sizeWithin.y};

				glGenVertexArrays(1, &tempIm.vertexArray);
				glBindVertexArray(tempIm.vertexArray);

				glGenBuffers(1, &tempIm.vertexBuffer);
				glBindBuffer(GL_ARRAY_BUFFER, tempIm.vertexBuffer);
				glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*16, bufferData, GL_DYNAMIC_DRAW);

				glEnableVertexAttribArray(vertexPositionLoc);
				glEnableVertexAttribArray(baseTexCoordsLoc);
				glVertexAttribPointer(vertexPositionLoc,
										2,
										GL_FLOAT,
										GL_FALSE,
										sizeof(GLfloat)*4,
										0);
				glVertexAttribPointer(baseTexCoordsLoc,
										2,
										GL_FLOAT,
										GL_FALSE,
										sizeof(GLfloat)*4,
										(void *)(sizeof(GLfloat)*2));

				glBindVertexArray(0);
				lastVAO = 0;
			}

			images.insert(make_pair(atlasImage->name, tempIm));

			delete [] imageData;

			DebugStatement(L"GLESDrawer: Glyph inserted into existing atlas.");
		}
		retval = metrics.find(missingCharacter);
	}
	else
		DebugStatement(L"GLESDrawer: Could not load missing glyph: " << missingCharacter << L" for font: " << fontId);

	return retval;
}

//------------------------------------------------------------------------------
void GLESDrawer::beginDrawing()
{
	if(clearBetweenFrames)
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	matrix.identity();
	glUniformMatrix4fv(shaders[currentShader].matrixLoc,
					   1,
					   GL_FALSE,
					   matrix.getMatrix());
}

//------------------------------------------------------------------------------
void GLESDrawer::endDrawing()
{
	map<TextLayout *, TextLayoutData>::iterator it;
	map<TextLayout *, TextLayoutData>::iterator it2;

	lastTexture = 0;

	matrix.clear();

	//Do any TextLayoutData garbage collection.
	for(it=cachedTextLayoutData.begin();it!=cachedTextLayoutData.end();)
	{
		it2 = it;
		++it;

		++(it2->second.gcCount);
		if(it2->second.gcCount > 1800) //For 60fps this should be ~30 seconds.
		{
			glDeleteVertexArrays(1, &(it2->second.textVAO));
			cachedTextLayoutData.erase(it2);
		}
	}
}

//------------------------------------------------------------------------------
void GLESDrawer::setClearBetweenFrames(bool val)
{
	clearBetweenFrames = val;
}

//------------------------------------------------------------------------------
void GLESDrawer::getScreenPixels(vector<uint8_t>& pixelBuffer)
{
	const unsigned int bufferSize = (unsigned int)windowSize.x*(unsigned int)windowSize.y*3;

	//Make sure the buffer has enough space for what we're doing.
	if(pixelBuffer.size() < bufferSize)
		pixelBuffer.reserve(bufferSize);

	//So we don't have to call this after Drawer::endDrawing().
	//SDL_GL_SwapBuffers();
	glFinish(); //Will this do the same thing as SDL_GL_SwapBuffers()?

	//Clear the buffer.
	pixelBuffer.assign(bufferSize, 0);

	//Get pixels from opengl frame buffer.
	glReadPixels(0,					//X start pos.
				 0,					//Y start pos.
				 (int)windowSize.x,		//Width.
				 (int)windowSize.y,		//Height.
				 GL_RGB,			//What data we're reading.
				 GL_UNSIGNED_BYTE,	//Format to write to.
#ifndef OSX //I hate apple. Their STL implementation is pathetic.
				 (void *)pixelBuffer.data());	//Buffer to write to.
#else
				 (void *)(&pixelBuffer[0]));
#endif
}

//------------------------------------------------------------------------------
void GLESDrawer::useShader(const wstring& shader)
{
	if(shader == L"")
		currentShader = L"-default-shader-";
	else
		currentShader = shader;

	assert(shaders.find(currentShader) != shaders.end());

	glUseProgram(shaders[currentShader].shader);

	/*if(shader != L"-default-shader-")
	{
		ESImage *image1;

		image1 = &(images[L"-frame-buffer-image-"]);

		glBindVertexArray(image1->vertexArray);
		glBindBuffer(GL_ARRAY_BUFFER, image1->vertexBuffer);

		glEnableVertexAttribArray(vertexPositionLoc);
		glEnableVertexAttribArray(baseTexCoordsLoc);
		glVertexAttribPointer(vertexPositionLoc,
								2,
								GL_FLOAT,
								GL_FALSE,
								sizeof(GLfloat)*4,
								0);
		glVertexAttribPointer(baseTexCoordsLoc,
								2,
								GL_FLOAT,
								GL_FALSE,
								sizeof(GLfloat)*4,
								(void *)(sizeof(GLfloat)*2));

		glBindVertexArray(0);
		lastVAO = 0;
	}*/
}

//------------------------------------------------------------------------------
void GLESDrawer::setShaderParam(const std::wstring& param, float val)
{
	glUniform1f(shaders[currentShader].uniformLocs[param], val);
}

//------------------------------------------------------------------------------
void GLESDrawer::setBlendMode(int val)
{
	switch(val)
	{
		case AlphaBlend:
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glBlendEquation(GL_FUNC_ADD);
			break;
		case Additive:
			glBlendFunc(GL_SRC_ALPHA, GL_ONE);
			glBlendEquation(GL_FUNC_ADD);
			break;
		default:
			{
				map<int, CustomBlendMode *>::iterator it = customBlendModes.find(val);

				if(it != customBlendModes.end())
					it->second->setBlendMode();
				else
					DebugStatement(L"GLESDrawer: Could not find custom blend mode: " << val);
			}
			break;
	}
}

//------------------------------------------------------------------------------
void GLESDrawer::registerCustomBlendMode(int identifier,
												CustomBlendMode *mode)
{
	customBlendModes.insert(make_pair(identifier, mode));
}

//------------------------------------------------------------------------------
void GLESDrawer::drawImage(const wstring& name,
								  const TwoFloats& position,
								  const TwoFloats& size,
								  const ThreeFloats& colour,
								  float alpha)
{
	ESImage *image1;
	ShaderInternal *shader;

#ifdef DEBUG
	map<wstring, ESImage>::iterator it;
	map<wstring, ShaderInternal>::iterator it2;

	it = images.find(name);
	it2 = shaders.find(currentShader);

	assert(it != images.end());
	assert(it2 != shaders.end());
#endif

	image1 = &(images[name]);
	shader = &(shaders[currentShader]);

	//Store the current model matrix.
	matrix.push();

	//Translate to the position.
	matrix.translate(position.x, position.y, 0.0f);

	//Scale the image accordingly.
	if(size != 1.0f)
		matrix.scale(size.x, size.y, 1.0f);
	glUniformMatrix4fv(shader->matrixLoc, 1, GL_FALSE, matrix.getMatrix());

	if(lastTexture != image1->texture)
	{
		glBindTexture(GL_TEXTURE_2D, image1->texture);
		glUniform1f(shader->textureExistsLoc, 1.0f);
		lastTexture = image1->texture;
	}
	if(lastVAO != image1->vertexArray)
	{
		glBindVertexArray(image1->vertexArray);
		lastVAO = image1->vertexArray;
	}
	
	glUniform4f(shader->colourLoc, colour.x*alpha, colour.y*alpha, colour.z*alpha, alpha);
	glDrawArrays(GL_TRIANGLE_STRIP, image1->arrayOffset, 4);

	matrix.pop();
	glUniformMatrix4fv(shader->matrixLoc, 1, GL_FALSE, matrix.getMatrix());
}

//------------------------------------------------------------------------------
void GLESDrawer::drawRotImage(const wstring& name,
							  const TwoFloats& position,
							  float angle,
							  const TwoFloats& pivot,
							  const TwoFloats& size,
							  const ThreeFloats& colour,
							  float alpha)
{
	ESImage *image1;
	ShaderInternal *shader;

#ifdef DEBUG
	map<wstring, ESImage>::iterator it;
	map<wstring, ShaderInternal>::iterator it2;

	it = images.find(name);
	it2 = shaders.find(currentShader);

	assert(it != images.end());
	assert(it2 != shaders.end());
#endif

	image1 = &(images[name]);
	shader = &(shaders[currentShader]);

	//Store the current model matrix.
	matrix.push();

	//Translate to the position.
	matrix.translate(position.x, position.y, 0.0f);

	//Rotate around the pivot point.
	matrix.translate(pivot.x*size.x, pivot.y*size.y, 0.0f);
	matrix.rotate(-angle, 0.0f, 0.0f, 1.0f);
	matrix.translate(-pivot.x*size.x, -pivot.y*size.y, 0.0f);

	//Scale the image accordingly.
	if(size != 1.0f)
		matrix.scale(size.x, size.y, 1.0f);

	glUniformMatrix4fv(shader->matrixLoc, 1, GL_FALSE, matrix.getMatrix());

	if(lastTexture != image1->texture)
	{
		glBindTexture(GL_TEXTURE_2D, image1->texture);
		glUniform1f(shader->textureExistsLoc, 1.0f);
		lastTexture = image1->texture;
	}
	if(lastVAO != image1->vertexArray)
	{
		glBindVertexArray(image1->vertexArray);
		lastVAO = image1->vertexArray;
	}

	glUniform4f(shader->colourLoc, colour.x*alpha, colour.y*alpha, colour.z*alpha, alpha);
	glDrawArrays(GL_TRIANGLE_STRIP, image1->arrayOffset, 4);

	matrix.pop();
	glUniformMatrix4fv(shader->matrixLoc, 1, GL_FALSE, matrix.getMatrix());
}

//------------------------------------------------------------------------------
void GLESDrawer::drawSubImage(const wstring& name,
									 const TwoFloats& position,
									 const TwoFloats& subPos,
									 const TwoFloats& subSize,
									 const TwoFloats& size,
									 const ThreeFloats& colour,
									 float alpha)
{
	ESImage *image1;
	TwoFloats imageSize;
	ShaderInternal *shader;
	GLfloat texCoords[] = {0.0f, 0.0f,
						   0.0f, 1.0f,
						   1.0f, 0.0f,
						   1.0f, 1.0f};

#ifdef DEBUG
	map<wstring, ESImage>::iterator it;
	map<wstring, ShaderInternal>::iterator it2;

	it = images.find(name);
	it2 = shaders.find(currentShader);

	assert(it != images.end());
	assert(it2 != shaders.end());
#endif

	image1 = &(images[name]);
	shader = &(shaders[currentShader]);

	//Calculate the actual texture coordinates.
	//(lower left)
	texCoords[0] = image1->posWithin.x + (subPos.x * image1->sizeWithin.x);
	texCoords[1] = image1->posWithin.y + (subPos.y * image1->sizeWithin.y);
	//(upper left)
	texCoords[2] = image1->posWithin.x + (subPos.x * image1->sizeWithin.x);
	texCoords[3] = image1->posWithin.y + ((subPos.y+subSize.y) * image1->sizeWithin.y);
	//(lower right)
	texCoords[4] = image1->posWithin.x + ((subPos.x+subSize.x) * image1->sizeWithin.x);
	texCoords[5] = image1->posWithin.y + (subPos.y * image1->sizeWithin.y);
	//(upper right)
	texCoords[6] = image1->posWithin.x + ((subPos.x+subSize.x) * image1->sizeWithin.x);
	texCoords[7] = image1->posWithin.y + ((subPos.y+subSize.y) * image1->sizeWithin.y);

	//Store the current model matrix.
	matrix.push();

	//Translate to the position.
	matrix.translate(position.x, position.y, 0.0f);

	//Scale the image accordingly.
	matrix.scale(size.x*image1->size.x*subSize.x,
				 size.y*image1->size.y*subSize.y,
				 1.0f);
	glUniformMatrix4fv(shader->matrixLoc, 1, GL_FALSE, matrix.getMatrix());

	if(lastTexture != image1->texture)
	{
		glBindTexture(GL_TEXTURE_2D, image1->texture);
		glUniform1f(shader->textureExistsLoc, 1.0f);
		lastTexture = image1->texture;
	}
	if(lastVAO != texCoordsVAO)
	{
		glBindVertexArray(texCoordsVAO);
		lastVAO = texCoordsVAO;
	}

	//Upload the new texture coordinates.
	glBindBuffer(GL_ARRAY_BUFFER, texCoordsVBO);
	glBufferData(GL_ARRAY_BUFFER,
				 sizeof(GLfloat)*8,
				 NULL,
				 GL_STREAM_DRAW);
	glBufferSubData(GL_ARRAY_BUFFER,
					0,
					sizeof(GLfloat)*8,
					texCoords);
	
	glUniform4f(shader->colourLoc, colour.x*alpha, colour.y*alpha, colour.z*alpha, alpha);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

	matrix.pop();
	glUniformMatrix4fv(shader->matrixLoc, 1, GL_FALSE, matrix.getMatrix());
}

//------------------------------------------------------------------------------
void GLESDrawer::drawTiledImage(const wstring& name,
									   const TwoFloats& position,
									   const TwoFloats& size,
									   const TwoFloats& tilePos,
									   const TwoFloats& tileNum,
									   const ThreeFloats& colour,
									   float alpha)
{
	ESImage *image1;
	TwoFloats imageSize;
	ShaderInternal *shader;
	GLfloat texCoords[] = {tilePos.x,			tilePos.y,
						   tilePos.x,			tilePos.y+tileNum.y,
						   tilePos.x+tileNum.x,	tilePos.y,
						   tilePos.x+tileNum.x,	tilePos.y+tileNum.y};

#ifdef DEBUG
	map<wstring, ESImage>::iterator it;
	map<wstring, ShaderInternal>::iterator it2;

	it = images.find(name);
	it2 = shaders.find(currentShader);

	assert(it != images.end());
	assert(it2 != shaders.end());
#endif

	image1 = &(images[name]);
	shader = &(shaders[currentShader]);

	//Store the current model matrix.
	matrix.push();

	//Translate to the position.
	matrix.translate(position.x, position.y, 0.0f);

	//Scale the image accordingly.
	matrix.scale(size.x, size.y, 1.0f);
	glUniformMatrix4fv(shader->matrixLoc, 1, GL_FALSE, matrix.getMatrix());

	if(lastTexture != image1->texture)
	{
		glBindTexture(GL_TEXTURE_2D, image1->texture);
		glUniform1f(shader->textureExistsLoc, 1.0f);
		lastTexture = image1->texture;

		//Make sure the texture will tile.
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	}
	if(lastVAO != texCoordsVAO)
	{
		glBindVertexArray(texCoordsVAO);
		lastVAO = texCoordsVAO;
	}

	//Upload the new texture coordinates.
	glBindBuffer(GL_ARRAY_BUFFER, texCoordsVBO);
	glBufferData(GL_ARRAY_BUFFER,
				 sizeof(GLfloat)*8,
				 NULL,
				 GL_STREAM_DRAW);
	glBufferSubData(GL_ARRAY_BUFFER,
					0,
					sizeof(GLfloat)*8,
					texCoords);
	
	glUniform4f(shader->colourLoc, colour.x*alpha, colour.y*alpha, colour.z*alpha, alpha);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

	matrix.pop();
	glUniformMatrix4fv(shader->matrixLoc, 1, GL_FALSE, matrix.getMatrix());
}

//------------------------------------------------------------------------------
void GLESDrawer::drawText(const wstring& text,
						  const wstring& font,
						  const TwoFloats& position,
						  float boxWidth,
						  TextLayout::Justification justification,
						  float size,
						  const ThreeFloats& colour,
						  float alpha)
{
	TwoFloats pos;
	unsigned int i;
	TwoFloats scale(size, size);
	map<wstring, FontInternal>::iterator it;

	it = fonts.find(font);
	if(it != fonts.end())
	{
		const vector<TextLayout::GlyphPos>& glyphs = textLayout.getLayoutForText(text,
																				 it->second.metrics,
																				 boxWidth,
																				 size,
																				 justification,
																				 font,
																				 this);

		for(i=0;i<glyphs.size();++i)
		{
			pos = glyphs[i].position + position;
			drawImage(glyphs[i].image, pos, scale, colour, alpha);
		}
	}
}

//------------------------------------------------------------------------------
void GLESDrawer::drawText(const wstring& text,
						  const wstring& font,
						  TextLayout& layout,
						  const TwoFloats& position,
						  float boxWidth,
						  TextLayout::Justification justification,
						  float size,
						  const ThreeFloats& colour,
						  float alpha)
{
	TwoFloats pos;
	TwoFloats scale(size, size);
	map<wstring, FontInternal>::iterator it;
	map<TextLayout *, TextLayoutData>::iterator it2;

	it = fonts.find(font);
	if(it != fonts.end())
	{
		//If layout is empty, fill it up.
		if(layout.getUninitialised())
		{
			//Get the glyph positions.
			const vector<TextLayout::GlyphPos>& glyphs = layout.getLayoutForText(text,
																				 it->second.metrics,
																				 boxWidth,
																				 1.0f,
																				 justification,
																				 font,
																				 this);
			//Create the VAO and texture data for this text.
			createTextLayoutData(&layout, glyphs);
			it2 = cachedTextLayoutData.find(&layout);

			assert(it2 != cachedTextLayoutData.end());

			//Draw the text.
			drawCachedTextLayout(it2->second, position, size, colour, alpha);
		}
		else //Otherwise, draw it as-is.
		{
			it2 = cachedTextLayoutData.find(&layout);

			//If the VAO data's been garbage collected, reload it.
			if(it2 == cachedTextLayoutData.end())
			{
				const vector<TextLayout::GlyphPos>& glyphs = layout.getExistingLayout();

				createTextLayoutData(&layout, glyphs);
				it2 = cachedTextLayoutData.find(&layout);
				assert(it2 != cachedTextLayoutData.end());
			}

			//Draw the text.
			drawCachedTextLayout(it2->second, position, size, colour, alpha);
		}
	}
}

//------------------------------------------------------------------------------
void GLESDrawer::drawLine(const wstring& image,
						  const TwoFloats& start,
						  const TwoFloats& end,
						  const ThreeFloats& colour,
						  float alpha)
{
	ESImage *image1;
	ShaderInternal *shader;
	map<wstring, ESImage>::iterator it;
	const float angle = start.getAngle(end);
	const float dist = sqrtf(((end.x-start.x)*(end.x-start.x)) + ((end.y-start.y)*(end.y-start.y)));

	it = images.find(image);
	if(it == images.end())
		it = images.find(L"-default-line-");
	image1 = &(it->second);
	shader = &(shaders[currentShader]);

	if(lastTexture != image1->texture)
	{
		glBindTexture(GL_TEXTURE_2D, image1->texture);
		lastTexture = image1->texture;
	}
	if(lastVAO != image1->vertexArray)
	{
		glBindVertexArray(image1->vertexArray);
		lastVAO = image1->vertexArray;
	}

	//Store the current model matrix.
	matrix.push();

	//Move the line to the start point, rotate it, and stretch it.
	matrix.translate(start.x-4.0f, start.y, 0.0f);

	matrix.translate(4.0f, 0.0f, 0.0f);
	matrix.rotate(angle, 0.0f, 0.0f, 1.0f);
	matrix.translate(-4.0f, 0.0f, 0.0f);

	matrix.scale(1.0f, (dist*0.125f), 0.0f);

	//Push the matrix onto the GPU.
	glUniformMatrix4fv(shader->matrixLoc, 1, GL_FALSE, matrix.getMatrix());
	
	//Draw the line.
	glUniform4f(shader->colourLoc, colour.x*alpha, colour.y*alpha, colour.z*alpha, alpha);
	glDrawArrays(GL_TRIANGLE_STRIP, image1->arrayOffset, 4);

	//Pop the matrix.
	matrix.pop();
	glUniformMatrix4fv(shader->matrixLoc, 1, GL_FALSE, matrix.getMatrix());
}

//------------------------------------------------------------------------------
void GLESDrawer::drawConnectedLine(const wstring& image,
								   const vector<TwoFloats>& points,
								   const vector<float>& pointsScale,
								   const vector<ThreeFloats>& pointsColour,
								   const vector<float>& pointsAlpha)
{
	unsigned int i;
	ShaderInternal *shader;
	unsigned int connectedIndex = 0;
	map<wstring, ESImage>::iterator it;
	//The two intersecting lines where the line we're drawing meets its
	//neighbours.
	TwoFloats intersections[2];

	it = images.find(image);
	if(it == images.end())
		it = images.find(L"-default-line-");
	shader = &(shaders[currentShader]);

	if(lastTexture != it->second.texture)
	{
		glBindTexture(GL_TEXTURE_2D, it->second.texture);
		lastTexture = it->second.texture;
	}
	if(lastVAO != connectedVAO)
	{
		glBindVertexArray(connectedVAO);
		lastVAO = connectedVAO;
	}

	//Check the data we've been passed doesn't exceed the size of our buffers.
	if(points.size() > (connectedData.size()/12))
	{
		vector<float> textureData;
		unsigned int newSize = points.size();

		//Fill out connectedData.
		for(i=(connectedData.size()/12);i<newSize;++i)
		{
			//Vertex 0.
			connectedData.push_back(0.0f);
			connectedData.push_back(0.0f);

			//Colour 0.
			connectedData.push_back(0.0f);
			connectedData.push_back(0.0f);
			connectedData.push_back(0.0f);
			connectedData.push_back(1.0f);

			//Vertex 1.
			connectedData.push_back(0.0f);
			connectedData.push_back(0.0f);

			//Colour 1.
			connectedData.push_back(0.0f);
			connectedData.push_back(0.0f);
			connectedData.push_back(0.0f);
			connectedData.push_back(1.0f);
		}

		//Fill out textureData.
		for(i=0;i<newSize/2;++i)
		{
			//Tex coord 0.
			textureData.push_back(0.0f);
			textureData.push_back(0.0f);

			//Tex coord 1.
			textureData.push_back(1.0f);
			textureData.push_back(0.0f);

			//Tex coord 2.
			textureData.push_back(0.0f);
			textureData.push_back(1.0f);

			//Tex coord 3.
			textureData.push_back(1.0f);
			textureData.push_back(1.0f);
		}

		//Upload the texture data.
		glBindBuffer(GL_ARRAY_BUFFER, connectedTexVBO);
		glBufferData(GL_ARRAY_BUFFER,
					 sizeof(GLfloat)*textureData.size(),
					 &(textureData[0]),
					 GL_STATIC_DRAW);
	}

	intersections[1] = (points[1] - points[0]).perpendicular().normal();
	for(i=0;i<(points.size()-2);++i)
	{
		//The 4 trail points we need to calculate the correct corners for the
		//line trailPos[i] -> trailPos[i+1].
		TwoFloats a1(points[i]);
		TwoFloats a2(points[i+1]);
		TwoFloats a3(points[i+2]);

		//The 4 corners of the quad we use to draw the line
		//points[i] -> points[i+1]. The arrangement is:
		//1--3
		//|..|
		//|..|
		//0--2
		TwoFloats corners[2];

		//Start with the 'bottom-left' corner.
		intersections[0] = intersections[1];
		corners[0] = a1 - (intersections[0] * pointsScale[i]);

		//Now the 'top-left' corner.
		corners[1] = a1 + (intersections[0] * pointsScale[i]);

		//Now the 'bottom-right' corner.
		intersections[1] = (a3-a2).perpendicular().normal();
		//corners[2] = a2 - (intersections[1] * pointsScale[i+1]);

		//Now the 'top-right' corner.
		//corners[3] = a2 + (intersections[1] * pointsScale[i+1]);

		//'bottom-left' vertex data.
		connectedData[connectedIndex] = corners[0].x;
		++connectedIndex;
		connectedData[connectedIndex] = corners[0].y;
		++connectedIndex;

		//'bottom-left' colour data.
		connectedData[connectedIndex] = pointsColour[i].x * pointsAlpha[i];
		++connectedIndex;
		connectedData[connectedIndex] = pointsColour[i].y * pointsAlpha[i];
		++connectedIndex;
		connectedData[connectedIndex] = pointsColour[i].z * pointsAlpha[i];
		++connectedIndex;
		connectedData[connectedIndex] = pointsAlpha[i];
		++connectedIndex;

		//'top-left' vertex data.
		connectedData[connectedIndex] = corners[1].x;
		++connectedIndex;
		connectedData[connectedIndex] = corners[1].y;
		++connectedIndex;

		//'top-left' colour data.
		connectedData[connectedIndex] = pointsColour[i].x * pointsAlpha[i];
		++connectedIndex;
		connectedData[connectedIndex] = pointsColour[i].y * pointsAlpha[i];
		++connectedIndex;
		connectedData[connectedIndex] = pointsColour[i].z * pointsAlpha[i];
		++connectedIndex;
		connectedData[connectedIndex] = pointsAlpha[i];
		++connectedIndex;
	}

	//Upload the new vertex data to the VBO.
	glBindBuffer(GL_ARRAY_BUFFER, connectedVBO);
	glBufferData(GL_ARRAY_BUFFER,
				 sizeof(GLfloat)*connectedIndex,
				 NULL,
				 GL_STREAM_DRAW);
	glBufferSubData(GL_ARRAY_BUFFER,
					0,
					sizeof(GLfloat)*connectedIndex,
					&(connectedData[0]));

	//Set the useVertexColour uniform.
	glUniform1f(shader->useVertexColourLoc, 1.0f);

	//Draw the line.
	glDrawArrays(GL_TRIANGLE_STRIP, 0, (points.size()-2)*2);

	//Reset the useVertexColour uniform.
	glUniform1f(shader->useVertexColourLoc, 0.0f);
}

//------------------------------------------------------------------------------
void GLESDrawer::fillRect(const TwoFloats& position,
						  const TwoFloats& size,
						  const ThreeFloats& colour,
						  float alpha)
{
	ShaderInternal *shader;

#ifdef DEBUG
	map<wstring, ShaderInternal>::iterator it;

	it = shaders.find(currentShader);
	assert(it != shaders.end());
#endif

	shader = &(shaders[currentShader]);

	//Unbind current texture first.
	glBindTexture(GL_TEXTURE_2D, 0);
	lastTexture = 0;

	//Store the current model matrix.
	matrix.push();

	//Translate to the position.
	matrix.translate(position.x, position.y, 0.0f);

	//Scale the image accordingly.
	matrix.scale(size.x, size.y, 1.0f);
	glUniformMatrix4fv(shader->matrixLoc, 1, GL_FALSE, matrix.getMatrix());

	//Make sure we don't try and do textured rendering.
	glUniform1f(shader->textureExistsLoc, 0.0f);
	glUniform1f(shader->useVertexColourLoc, 0.0f);
	glUniform4f(shader->colourLoc, colour.x*alpha, colour.y*alpha, colour.z*alpha, alpha);

	//Draw the rect.
	if(lastVAO != rectVAO)
	{
		glBindVertexArray(rectVAO);
		lastVAO = rectVAO;
	}
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

	//Reset to textured rendering.
	glUniform1f(shader->textureExistsLoc, 1.0f);

	//Reset the modelview matrix.
	matrix.pop();
	glUniformMatrix4fv(shader->matrixLoc, 1, GL_FALSE, matrix.getMatrix());
}

//------------------------------------------------------------------------------
void GLESDrawer::fillGradient(const TwoFloats& position,
							  const TwoFloats& size,
							  const ThreeFloats& colourTop,
							  const ThreeFloats& colourBottom,
							  float alphaTop,
							  float alphaBottom)
{
	ShaderInternal *shader;
	GLfloat colourData[] = {colourTop.x*alphaTop, colourTop.y*alphaTop, colourTop.z*alphaTop, alphaTop,
							colourBottom.x*alphaBottom, colourBottom.y*alphaBottom, colourBottom.z*alphaBottom, alphaBottom,
							colourTop.x*alphaTop, colourTop.y*alphaTop, colourTop.z*alphaTop, alphaTop,
							colourBottom.x*alphaBottom, colourBottom.y*alphaBottom, colourBottom.z*alphaBottom, alphaBottom};

#ifdef DEBUG
	map<wstring, ShaderInternal>::iterator it;

	it = shaders.find(currentShader);
	assert(it != shaders.end());
#endif

	shader = &(shaders[currentShader]);

	//Store the current model matrix.
	matrix.push();

	//Translate to the position.
	matrix.translate(position.x, position.y, 0.0f);

	//Scale the image accordingly.
	matrix.scale(size.x, size.y, 1.0f);
	glUniformMatrix4fv(shader->matrixLoc, 1, GL_FALSE, matrix.getMatrix());

	//Make sure we don't try and do textured rendering.
	glUniform1f(shader->textureExistsLoc, 0.0f);

	//Make sure we use the vertex colour data.
	glUniform1f(shader->useVertexColourLoc, 1.0f);

	//Upload the colour data.
	glBindBuffer(GL_ARRAY_BUFFER, rectVBO);
	glBufferData(GL_ARRAY_BUFFER,
				 sizeof(GLfloat)*16,
				 NULL,
				 GL_STREAM_DRAW);
	glBufferSubData(GL_ARRAY_BUFFER,
					0,
					sizeof(GLfloat)*16,
					colourData);

	//Draw the rect.
	if(lastVAO != rectVAO)
	{
		glBindVertexArray(rectVAO);
		lastVAO = rectVAO;
	}
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

	//Reset to not using the vertex colour data.
	glUniform1f(shader->useVertexColourLoc, 0.0f);

	//Reset to textured rendering.
	glUniform1f(shader->textureExistsLoc, 1.0f);

	//Reset the modelview matrix.
	matrix.pop();
	glUniformMatrix4fv(shader->matrixLoc, 1, GL_FALSE, matrix.getMatrix());
}

//------------------------------------------------------------------------------
void GLESDrawer::translate(const TwoFloats& offset)
{
	matrix.translate(offset.x, offset.y, 0.0f);
	glUniformMatrix4fv(shaders[currentShader].matrixLoc, 1, GL_FALSE, matrix.getMatrix());
}

//------------------------------------------------------------------------------
void GLESDrawer::scale(const TwoFloats& size)
{
	matrix.scale(size.x, size.y, 0.0f);
	glUniformMatrix4fv(shaders[currentShader].matrixLoc, 1, GL_FALSE, matrix.getMatrix());
}

//------------------------------------------------------------------------------
void GLESDrawer::rotate(float angle, const TwoFloats& pivot)
{
	//Rotate around the pivot point.
	matrix.translate(pivot.x, pivot.y, 0.0f);
	matrix.rotate(-angle, 0.0f, 0.0f, 1.0f);
	matrix.translate(-pivot.x, -pivot.y, 0.0f);
	glUniformMatrix4fv(shaders[currentShader].matrixLoc, 1, GL_FALSE, matrix.getMatrix());
}

//------------------------------------------------------------------------------
void GLESDrawer::setViewport(const TwoFloats& position, const TwoFloats& size)
{
	int x2, y2, w2, h2;

	//Convert input coords to pixels.
	x2 = (int)((position.x/drawingSize.x) * viewportSize.x);
	y2 = (int)((position.y/drawingSize.y) * viewportSize.y);
	w2 = (int)((size.x/drawingSize.x) * viewportSize.x);
	h2 = (int)((size.y/drawingSize.y) * viewportSize.y);

	//Set the viewport.
	glViewport((int)viewportPos.x+x2,
			   (int)viewportPos.y+((int)viewportSize.y-(y2+h2)),
			   w2,
			   h2);

	//Now setup the projection matrix.
	//(so our co-ordinates for drawing are 0-size.x (x) and size.y-0 (y))
	projectionMatrix.generateOrthoProjection(0.0f, size.x, size.y, 0.0f);
	glUniformMatrix4fv(shaders[currentShader].projectionLoc,
					   1,
					   GL_FALSE,
					   projectionMatrix.getMatrix());
}

//------------------------------------------------------------------------------
void GLESDrawer::resetViewport()
{
	glViewport((int)viewportPos.x,
			   (int)viewportPos.y,
			   (int)viewportSize.x,
			   (int)viewportSize.y);

	//Reset the projection matrix.
	projectionMatrix.generateOrthoProjection(0.0f,
											 drawingSize.x,
											 drawingSize.y,
											 0.0f);
	glUniformMatrix4fv(shaders[currentShader].projectionLoc,
					   1,
					   GL_FALSE,
					   projectionMatrix.getMatrix());
}

//------------------------------------------------------------------------------
void GLESDrawer::startFrameBufferDrawing()
{
	glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);
	//glDrawBuffer(GL_COLOR_ATTACHMENT0); //Not present in OpenGL ES. Necessary?

	matrix.identity();
	glViewport(0, 0, (GLsizei)drawingSize.x, (GLsizei)drawingSize.y);
	projectionMatrix.generateOrthoProjection(0.0f, drawingSize.x, 0.0f, drawingSize.y);
	glUniformMatrix4fv(shaders[currentShader].projectionLoc,
					   1,
					   GL_FALSE,
					   projectionMatrix.getMatrix());
}

//------------------------------------------------------------------------------
void GLESDrawer::endFrameBufferDrawing()
{
	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	resetViewport();
}

//------------------------------------------------------------------------------
bool GLESDrawer::canSupport(GraphicsCapabilities feature) const
{
	bool retval = false;

#ifndef OSX
	if(!glewAlready)
	{
		GLenum err = glewInit();
		if(err != GLEW_OK)
		{
			wstring tempstr;

			UTF8File::charToWstring(string((const char *)glewGetErrorString(err)), tempstr);
			DebugStatement(L"GLESDrawer: Could not initialise GLEW: " << tempstr.c_str());
		}
		else
		{
			wstring tempstr;

			UTF8File::charToWstring(string((const char *)glewGetString(GLEW_VERSION)), tempstr);
			DebugStatement(L"GLESDrawer: GLEW initalised. Using version: " << tempstr);
			glewAlready = true;
		}
	}

	switch(feature)
	{
		case Shaders:
			if(GLEW_VERSION_2_0)
				retval = true;
			DebugStatement(L"GLESDrawer: canSupport(Shaders): " << (bool)retval);
			break;
		case FrameBuffer:
			if(GLEW_VERSION_2_0)
				retval = true;
			DebugStatement(L"GLESDrawer: canSupport(FrameBuffer): " << (bool)retval);
			break;
		case VertexArrayObjects:
			if(glewIsSupported("GL_ARB_vertex_array_object"))
				retval = true;
			DebugStatement(L"GLESDrawer: canSupport(VertexArrayObjects): " << (bool)retval);
			break;
	}
#else
	switch(feature)
	{
		case Shaders:
		case FrameBuffer:
			{
				float version;
				stringstream tempstr;
				const GLubyte *versionString = glGetString(GL_VERSION);

				tempstr << versionString;
				tempstr >> version;

				if(version >= 2.0f)
					retval = true;

				if(feature == Shaders)
				{
					DebugStatement(L"GLESDrawer: canSupport(Shaders): " << (bool)retval);
				}
				else
					DebugStatement(L"GLESDrawer: canSupport(FrameBuffer): " << (bool)retval);
			}
			break;
		case VertexArrayObjects:
			{
				string tempstr;
				const GLubyte *extensionString = glGetString(GL_EXTENSIONS);

				tempstr = (const char *)extensionString;

				if(tempstr.find("GL_ARB_vertex_array_object"))
					retval = true;
				DebugStatement(L"GLESDrawer: canSupport(VertexArrayObjects): " << (bool)retval);
			}
			break;
	}
#endif

	return retval;
}

//------------------------------------------------------------------------------
bool GLESDrawer::isSupported()
{
#ifndef OSX
	if(!glewAlready)
	{
		GLenum err = glewInit();
		if(err != GLEW_OK)
		{
			wstring tempstr;

			UTF8File::charToWstring(string((const char *)glewGetErrorString(err)), tempstr);
			DebugStatement(L"GLESDrawer: Could not initialise GLEW: " << tempstr.c_str());
		}
		else
		{
			wstring tempstr;

			UTF8File::charToWstring(string((const char *)glewGetString(GLEW_VERSION)), tempstr);
			DebugStatement(L"GLESDrawer: GLEW initalised. Using version: " << tempstr);
			glewAlready = true;
		}
	}

	return (GLEW_VERSION_2_0 && glewIsSupported("GL_ARB_vertex_array_object"));
#else
	float version;
	string tempstr2;
	stringstream tempstr;
	const GLubyte *versionString = glGetString(GL_VERSION);
	const GLubyte *extensionString = glGetString(GL_EXTENSIONS);

	tempstr << versionString;
	tempstr >> version;

	tempstr2 = (const char *)extensionString;

	return ((version >= 2.0f) && (tempstr2.find("GL_ARB_vertex_array_object")));
#endif
}

//------------------------------------------------------------------------------
const Drawer::Image * const GLESDrawer::getInternalImageData(const wstring& imageId) const
{
	map<wstring, ESImage>::const_iterator it;

	it = images.find(imageId);
	if(it != images.end())
		return &(it->second);
	else
		return 0;
}

//------------------------------------------------------------------------------
void GLESDrawer::resetInternalCache()
{
	lastTexture = 0;
	lastVAO = 0;
}

//------------------------------------------------------------------------------
void GLESDrawer::printInfoLog(GLuint shader, bool program)
{
	string infoLog;
	wstring wInfoLog;
	char *infoLogChar;
	GLint infologLength = 0;
    GLsizei charsWritten = 0;
	
	if(program)
		glGetProgramiv(shader, GL_INFO_LOG_LENGTH, &infologLength);
	else
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infologLength);

	DebugStatement(L"infologLength = " << infologLength);
	
    if(infologLength > 0)
    {
		infoLog.resize(infologLength);
		infoLogChar = const_cast<char *>(infoLog.c_str());

		if(program)
			glGetProgramInfoLog(shader, infologLength, &charsWritten, infoLogChar);
		else
			glGetShaderInfoLog(shader, infologLength, &charsWritten, infoLogChar);

		UTF8File::charToWstring(infoLog, wInfoLog);
		DebugStatement(wInfoLog);
    }
}

//------------------------------------------------------------------------------
void GLESDrawer::createTextLayoutData(TextLayout *layout,
									  const vector<TextLayout::GlyphPos>& glyphs)
{
	unsigned int i, j;
	GLuint vertexBuffer;
	GLint bufferIndex = 0;
	std::vector<float> buffer;
	TextLayoutData *layoutData;
	map<GLuint, vector<int> >::iterator gtIt;
	map<TextLayout *, TextLayoutData>::iterator ctlIt;

	//We keep a vector of indices into glyphs for each texture we use to draw
	//the text.
	map<GLuint, vector<int> > glyphTextures;

	//Populate glyphTextures.
	for(i=0;i<glyphs.size();++i)
		glyphTextures[images[glyphs[i].image].texture].push_back(i);

	//If we already have an entry for this TextLayout, delete it.
	ctlIt = cachedTextLayoutData.find(layout);
	if(ctlIt != cachedTextLayoutData.end())
	{
		//Make sure we delete the VAO first.
		glDeleteVertexArrays(1, &ctlIt->second.textVAO);
		cachedTextLayoutData.erase(ctlIt);
	}

	//Create the new entry into cachedTextLayoutData.
	cachedTextLayoutData.insert(make_pair(layout, TextLayoutData()));
	ctlIt = cachedTextLayoutData.find(layout);
	assert(ctlIt != cachedTextLayoutData.end());
	layoutData = &(ctlIt->second);

	//Go through each texture we'll be drawing.
	for(gtIt=glyphTextures.begin();
		gtIt!=glyphTextures.end();
		++gtIt)
	{
		//Add a new TextureData to layoutData.
		layoutData->textureData.push_back(TextLayoutData::TextureData());

		//Set the texture and the offset into the VAO at which these vertices
		//appear.
		layoutData->textureData.back().texture = gtIt->first;
		layoutData->textureData.back().vaoOffset = (bufferIndex/4);

		//Go through each glyph which uses this texture and add the vertex
		//data to buffer.
		for(i=0;i<gtIt->second.size();++i)
		{
			const ESImage& tempIm = images[glyphs[gtIt->second[i]].image];
			const TextLayout::GlyphPos& tempGlyph = glyphs[gtIt->second[i]];

			//We have to duplicate the first and last vertices because we're
			//using triangle strips (this ensures we don't get weird polygons
			//generated in the gaps between glyphs).
			for(j=0;j<2;++j)
			{
				//Bottom left vertices.
				buffer.push_back(tempGlyph.position.x); //x
				buffer.push_back(tempGlyph.position.y); //y
				//Bottom left texture coordinates.
				buffer.push_back(tempIm.posWithin.x); //x
				buffer.push_back(tempIm.posWithin.y); //x
			}

			//Top left vertices.
			buffer.push_back(tempGlyph.position.x); //x
			buffer.push_back(tempGlyph.position.y + tempIm.size.y); //y
			//Top left texture coordinates.
			buffer.push_back(tempIm.posWithin.x); //x
			buffer.push_back(tempIm.posWithin.y + tempIm.sizeWithin.y); //x

			//Bottom right vertices.
			buffer.push_back(tempGlyph.position.x + tempIm.size.x); //x
			buffer.push_back(tempGlyph.position.y); //y
			//Bottom right texture coordinates.
			buffer.push_back(tempIm.posWithin.x + tempIm.sizeWithin.x); //x
			buffer.push_back(tempIm.posWithin.y); //x

			for(j=0;j<2;++j)
			{
				//Top right vertices.
				buffer.push_back(tempGlyph.position.x + tempIm.size.x); //x
				buffer.push_back(tempGlyph.position.y + tempIm.size.y); //y
				//Top right texture coordinates.
				buffer.push_back(tempIm.posWithin.x + tempIm.sizeWithin.x); //x
				buffer.push_back(tempIm.posWithin.y + tempIm.sizeWithin.y); //x
			}

			bufferIndex += 16 + 8;
		}

		//Set the number of GLfloats used to draw this text.
		layoutData->textureData.back().vaoCount = (bufferIndex - layoutData->textureData.back().vaoOffset)/4;
	}

	//Create the VBO and load the data into it.
	glGenBuffers(1, &vertexBuffer);
	glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
	glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*bufferIndex, &(buffer[0]), GL_STATIC_DRAW);

	//Create the VAO.
	glGenVertexArrays(1, &layoutData->textVAO);
	glBindVertexArray(layoutData->textVAO);
	
	glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
	glEnableVertexAttribArray(vertexPositionLoc);
	glEnableVertexAttribArray(baseTexCoordsLoc);
	glVertexAttribPointer(vertexPositionLoc,
							2,
							GL_FLOAT,
							GL_FALSE,
							sizeof(GLfloat)*4,
							0);
	glVertexAttribPointer(baseTexCoordsLoc,
							2,
							GL_FLOAT,
							GL_FALSE,
							sizeof(GLfloat)*4,
							(void *)(sizeof(GLfloat)*2));

	//lastVAO = layoutData->textVAO;
	glBindVertexArray(0);
	lastVAO = 0;
}

//------------------------------------------------------------------------------
void GLESDrawer::drawCachedTextLayout(TextLayoutData& layoutData,
									  const TwoFloats& position,
									  float size,
									  const ThreeFloats& colour,
									  float alpha)
{
	unsigned int i;
	ShaderInternal *shader;

	shader = &(shaders[currentShader]);

	//Store the current model matrix.
	matrix.push();

	//Translate to the position.
	matrix.translate(position.x, position.y, 0.0f);

	//Scale the image accordingly.
	if(size != 1.0f)
		matrix.scale(size, size, 1.0f);
	glUniformMatrix4fv(shader->matrixLoc, 1, GL_FALSE, matrix.getMatrix());

	//Bind VAO.
	if(lastVAO != layoutData.textVAO)
	{
		glBindVertexArray(layoutData.textVAO);
		lastVAO = layoutData.textVAO;
	}

	glUniform4f(shader->colourLoc, colour.x, colour.y, colour.z, alpha);
	for(i=0;i<layoutData.textureData.size();++i)
	{
		//Bind the texture.
		if(lastTexture != layoutData.textureData[i].texture)
		{
			glBindTexture(GL_TEXTURE_2D, layoutData.textureData[i].texture);
			glUniform1f(shader->textureExistsLoc, 1.0f);
			lastTexture = layoutData.textureData[i].texture;
		}

		//Draw this texture's vertex data.
		glDrawArrays(GL_TRIANGLE_STRIP,
					 layoutData.textureData[i].vaoOffset,
					 layoutData.textureData[i].vaoCount);
	}

	matrix.pop();
	glUniformMatrix4fv(shader->matrixLoc, 1, GL_FALSE, matrix.getMatrix());

	layoutData.gcCount = 0;
}

//------------------------------------------------------------------------------
bool GLESDrawer::glewAlready = false;

//------------------------------------------------------------------------------
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* GLESDrawer::line_png = (const char*)temp1;

//------------------------------------------------------------------------------
const wchar_t GLESDrawer::defaultVertexShader[] = {L"\
	#version 120\n\
	\n\
	attribute vec4 avlVertexPosition;\n\
	attribute vec4 avlVertexColour;\n\
	\n\
	uniform mat4x4 avlProjectionMatrix;\n\
	uniform mat4x4 avlModelviewMatrix;\n\
	\n\
	attribute vec2 avlBaseTexCoords;\n\
	\n\
	varying vec2 avlFragBaseTexCoords;\n\
	varying vec4 avlFragVertexColour;\n\
	\n\
	void main()\n\
	{\n\
		avlFragBaseTexCoords = avlBaseTexCoords;\n\
		avlFragVertexColour = avlVertexColour;\n\
		gl_Position = avlModelviewMatrix * avlVertexPosition * avlProjectionMatrix;\n\
	}\n\
"};
const wchar_t GLESDrawer::defaultFragmentShader[] = {L"\
	#version 120\n\
	\n\
	uniform vec4 avlColour;\n\
	\n\
	uniform sampler2D avlBaseTexture;\n\
	\n\
	uniform float avlTextureExists;\n\
	uniform float avlUseVertexColour;\n\
	\n\
	varying vec2 avlFragBaseTexCoords;\n\
	varying vec4 avlFragVertexColour;\n\
	\n\
	void main()\n\
	{\n\
		vec4 b = texture2D(avlBaseTexture, avlFragBaseTexCoords);\n\
		vec4 col = mix(avlColour, avlFragVertexColour, avlUseVertexColour);\n\
		\n\
		gl_FragColor = mix(col, col * b, avlTextureExists);\n\
	}\n\
"};
