//Simple Debug Font
//Liam Flookes 2013.
//No warranty or guarantees!
//Free to use for any purpose.

//Create the class after your OpenGL initialization
//Render method assumes shaders are bound and matrices are set up!

#include <unordered_map>
using std::unordered_map;

#include <string>
using std::string;

#include <OpenGLES/ES2/gl.h>

struct vector3
{
	vector3(float x=0.0f, float y=0.0f, float z=0.0f)
		:x(x),y(y),z(z) 
	{} 

	float x,y,z;
};

struct sRGBA
{
    sRGBA(float r=1.0f, float g=1.0f, float b=1.0f, float a=0.0f)
    :r(r),g(g),b(b),a(a)
    {}
    
	float r,g,b,a;
};

struct textureInfo
{
	textureInfo()
		:textureID(0), width(0), height(0)
	{}

	GLuint textureID;
	int width, height;
};

GLuint CreateTextureFromText(const char* text, const sRGBA &rgba, int &out_width, int &out_height);

typedef	unordered_map<string, textureInfo> tStringToTextureInfoMap;

class cDebugText
{
public:
	cDebugText();
	~cDebugText();

	void PurgeTextures();

	void RenderText(const string &texture, const sRGBA &rgba, int x, int y);

	static const int textVertexBufferSize = 8;
	
	GLuint mFontVertexBufferID;
	vector3 mFontVertexBuffer[textVertexBufferSize];
	
	tStringToTextureInfoMap mStringTextureMap;
};

cDebugText::cDebugText()
{
	glGenBuffers(1, &mFontVertexBufferID);
	mFontVertexBuffer[1] = vector3(0.0f, 0.0f, 0.0f);
	mFontVertexBuffer[3] = vector3(0.0f, 1.0f, 0.0f);
	mFontVertexBuffer[5] = vector3(1.0f, 0.0f, 0.0f);
	mFontVertexBuffer[7] = vector3(1.0f, 1.0f, 0.0f);
}

cDebugText::~cDebugText()
{
	glDeleteBuffers(1, &mFontVertexBufferID);
	PurgeTextures();
}

//RenderText assumes a shader with vertex and texture coordinates used are bound and correct matrices are set up!
void cDebugText::RenderText(const string &textToDraw, const sRGBA &rgba, int x, int y)
{
	textureInfo &texInfo = mStringTextureMap[textToDraw]; //will create an entry if one doesn't exist!
	
	if(texInfo.textureID == 0)
	{
		texInfo.textureID = CreateTextureFromText(textToDraw.c_str(), rgba, texInfo.height, texInfo.width);
		if(texInfo.textureID == 0)
			return; //problem making the texture!	
	}	

	float texHeight = texInfo.height;
	float texWidth = texInfo.width;

	mFontVertexBuffer[0]			= vector3(x, y);
	mFontVertexBuffer[2]			= vector3(x, y + texHeight);
	mFontVertexBuffer[4]			= vector3(x + texWidth, y);
	mFontVertexBuffer[6]			= vector3(x + texWidth, y + texHeight);

	glBindTexture( GL_TEXTURE_2D, texInfo.textureID );

	glBufferData(GL_ARRAY_BUFFER, textVertexBufferSize * sizeof(vector3), mFontVertexBuffer, GL_STREAM_DRAW);

	glVertexAttribPointer(0 /* Or your vertex attrib ID */, 3, GL_FLOAT, GL_FALSE, 2 * sizeof(vector3), 0);
	glVertexAttribPointer(1 /* Or your texcoord attrib ID */, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(vector3), (uint8_t*) sizeof(vector3));
	
	glDrawArrays( GL_TRIANGLE_STRIP, 0, 4);
}

void cDebugText::PurgeTextures()
{
	for ( auto texHand : mStringTextureMap )
	{
		glDeleteTextures(1, &texHand.second.textureID);
	}

	mStringTextureMap.clear();
}
		
//from bit twiddling hacks
inline uint32_t nextPowerOfTwo(uint32_t v)
{
    v--;
    v |= v >> 1;
    v |= v >> 2;
    v |= v >> 4;
    v |= v >> 8;
    v |= v >> 16;
    v++;
    return v;
}

GLuint CreateTextureFromText(const char* text, const sRGBA &rgba, int &out_width, int &out_height)
{    
    NSString *txt = [NSString stringWithUTF8String: text];
    UIFont *font = [UIFont fontWithName:@"Helvetica-Bold" size:16.0f];

    CGSize renderedSize = [txt sizeWithFont:font];

    const uint32_t height = nextPowerOfTwo((int)renderedSize.height); out_height = height;
    const uint32_t width = nextPowerOfTwo((int) renderedSize.width); out_width = width;
    const int bitsPerElement = 8;
    int sizeInBytes = height*width*4;
    int texturePitch = width*4;
    uint8_t *data = new uint8_t[sizeInBytes];
    memset(data, 0x00, sizeInBytes);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGContextRef context = CGBitmapContextCreate(data, width, height, bitsPerElement, texturePitch, colorSpace, kCGImageAlphaPremultipliedLast);

    CGContextSetTextDrawingMode(context, kCGTextFillStroke);

    float components[4] = { rgba.r, rgba.g, rgba.b, rgba.a };
    CGColorRef color = CGColorCreate(colorSpace, components);
    CGContextSetStrokeColorWithColor(context, color);    
    CGContextSetFillColorWithColor(context, color);        
    CGContextTranslateCTM(context, 0.0f, height);
    CGContextScaleCTM(context, 1.0f, -1.0f);

    UIGraphicsPushContext(context);

    [txt drawInRect:CGRectMake(0, 0, width, height) withFont:font lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentLeft];

    UIGraphicsPopContext();

    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);    

    GLuint textureID;
    glGenTextures(1, &textureID);    

    glBindTexture(GL_TEXTURE_2D, textureID);

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);     

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

    delete [] data;

    return textureID;
}		
		
