
// Repository: https://github.com/vallentin/glText
// License: https://github.com/vallentin/glText/blob/master/LICENSE.md
//
// Date Created: September 24, 2013
// Last Modified: July 02, 2020

// In one C or C++ file, define GLT_IMPLEMENTATION prior to inclusion to create the implementation.
//   #define GLT_IMPLEMENTATION
//   #include "gltext.h"

// Copyright (c) 2013-2018 Christian Vallentin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
//    be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
//    distribution.

// Refrain from using any exposed macros, functions
// or structs prefixed with an underscore. As these
// are only intended for internal purposes. Which
// additionally means they can be removed, renamed
// or changed between minor updates without notice.

#ifndef GL_TEXT_H
#define GL_TEXT_H

#ifdef __cplusplus
extern "C" {
#endif

#if !defined(__gl_h_) && !defined(__glcorearb_h_)
#	error OpenGL header must be included prior to including glText header
#endif

#include <stdlib.h> /* malloc(), calloc(), free() */
#include <string.h> /* memset(), memcpy(), strlen() */
#include <stdint.h> /* uint8_t, uint16_t, uint32_t, uint64_t */

#if (defined(_DEBUG) || defined(DEBUG)) && !defined(GLT_DEBUG)
#	define GLT_DEBUG 1
#endif

#ifdef GLT_DEBUG
#	include <assert.h> /* assert() */
#	define _GLT_ASSERT(expression) assert(expression)
#else
#	define _GLT_ASSERT(expression)
#endif

#ifdef GLT_DEBUG_PRINT
#	include <stdio.h> /* printf */
#endif

#define _GLT_STRINGIFY(str) #str
#define _GLT_STRINGIFY_TOKEN(str) _GLT_STRINGIFY(str)

#define GLT_STRINGIFY_VERSION(major, minor, patch) _GLT_STRINGIFY(major) "." _GLT_STRINGIFY(minor) "." _GLT_STRINGIFY(patch)

#define GLT_NAME "glText"

#define GLT_VERSION_MAJOR 1
#define GLT_VERSION_MINOR 1
#define GLT_VERSION_PATCH 6

#define GLT_VERSION GLT_STRINGIFY_VERSION(GLT_VERSION_MAJOR, GLT_VERSION_MINOR, GLT_VERSION_PATCH)
#define GLT_NAME_VERSION GLT_NAME " " GLT_VERSION

#define GLT_NULL 0
#define GLT_NULL_HANDLE 0

#ifdef GLT_IMPORTS
#	define GLT_API extern
#else
#	ifndef GLT_STATIC
#		define GLT_STATIC
#	endif
#	define GLT_API static
#endif

#define GLT_LEFT 0
#define GLT_TOP 0

#define GLT_CENTER 1

#define GLT_RIGHT 2
#define GLT_BOTTOM 2

static GLboolean gltInitialized = GL_FALSE;

typedef struct GLTtext GLTtext;

GLT_API GLboolean gltInit(void);
GLT_API void gltTerminate(void);

GLT_API GLTtext* gltCreateText(void);
GLT_API void gltDeleteText(GLTtext *text);
#define gltDestroyText gltDeleteText

GLT_API GLboolean gltSetText(GLTtext *text, const char *string);
// vvv GLT_API const char* gltGetText(GLTtext *text);

GLT_API void gltViewport(GLsizei width, GLsizei height);

GLT_API void gltBeginDraw();
GLT_API void gltEndDraw();

// vvv GLT_API void gltDrawText(GLTtext *text, const GLfloat mvp[16]);

GLT_API void gltDrawText2D(GLTtext *text, GLfloat x, GLfloat y, GLfloat scale);
GLT_API void gltDrawText2DAligned(GLTtext *text, GLfloat x, GLfloat y, GLfloat scale, int horizontalAlignment, int verticalAlignment);

// vvv GLT_API void gltDrawText3D(GLTtext *text, GLfloat x, GLfloat y, GLfloat z, GLfloat scale, GLfloat view[16], GLfloat projection[16]);

GLT_API void gltColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
// vvv GLT_API void gltGetColor(GLfloat *r, GLfloat *g, GLfloat *b, GLfloat *a);

GLT_API GLfloat gltGetLineHeight(GLfloat scale);

GLT_API GLfloat gltGetTextWidth(const GLTtext *text, GLfloat scale);
GLT_API GLfloat gltGetTextHeight(const GLTtext *text, GLfloat scale);

GLT_API GLboolean gltIsCharacterSupported(const char c);
// vvv GLT_API GLint gltCountSupportedCharacters(const char *str);

GLT_API GLboolean gltIsCharacterDrawable(const char c);
GLT_API GLint gltCountDrawableCharacters(const char *str);

GLT_API GLint gltCountNewLines(const char *str);

// After this point everything you'll see is the
// implementation and all the internal stuff.
// Use it at your own discretion, but be warned
// that everything can have changed in the next
// commit, so be careful relying on any of it.

#ifdef GLT_IMPLEMENTATION

#define _GLT_TEXT2D_POSITION_LOCATION 0
#define _GLT_TEXT2D_TEXCOORD_LOCATION 1

#define _GLT_TEXT2D_POSITION_SIZE 2
#define _GLT_TEXT2D_TEXCOORD_SIZE 2
#define _GLT_TEXT2D_VERTEX_SIZE (_GLT_TEXT2D_POSITION_SIZE + _GLT_TEXT2D_TEXCOORD_SIZE)

#define _GLT_TEXT2D_POSITION_OFFSET 0
#define _GLT_TEXT2D_TEXCOORD_OFFSET _GLT_TEXT2D_POSITION_SIZE

#define _GLT_MAT4_INDEX(row, column) ((row) + (column) * 4)

static const char *_gltFontGlyphCharacters = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!?-+/():;%&`*#=[]\"";
#define _gltFontGlyphCount 83

#define _gltFontGlyphMinChar ' '
#define _gltFontGlyphMaxChar 'z'

static const int _gltFontGlyphHeight = 17; // Line height

typedef struct _GLTglyph {
	char c;

	GLint x, y;
	GLint w, h;

	GLfloat u1, v1;
	GLfloat u2, v2;

	GLboolean drawable;
} _GLTglyph;

typedef struct _GLTglyphdata {
	uint32_t x, y;
	uint32_t w, h;

	uint32_t marginLeft, marginTop;
	uint32_t marginRight, marginBottom;

	uint16_t dataWidth, dataHeight;
} _GLTglyphdata;

static _GLTglyph _gltFontGlyphs[_gltFontGlyphCount];

#define _gltFontGlyphLength (_gltFontGlyphMaxChar - _gltFontGlyphMinChar + 1)
static _GLTglyph _gltFontGlyphs2[_gltFontGlyphLength];

static GLuint _gltText2DShader = GLT_NULL_HANDLE;
static GLuint _gltText2DFontTexture = GLT_NULL_HANDLE;

static GLint _gltText2DShaderMVPUniformLocation = -1;
static GLint _gltText2DShaderColorUniformLocation = -1;

static GLfloat _gltText2DProjectionMatrix[16];

struct GLTtext {
	char *_text;
	GLsizei _textLength;

	GLboolean _dirty;

	GLsizei vertexCount;
	GLfloat *_vertices;

	GLuint _vao;
	GLuint _vbo;
};

// vvv GLT_API void _gltGetViewportSize(GLint *width, GLint *height);

GLT_API void _gltMat4Mult(const GLfloat lhs[16], const GLfloat rhs[16], GLfloat result[16]);

GLT_API void _gltUpdateBuffers(GLTtext *text);

GLT_API GLboolean _gltCreateText2DShader(void);
GLT_API GLboolean _gltCreateText2DFontTexture(void);

GLT_API GLTtext* gltCreateText(void)
{
	GLTtext *text = (GLTtext*)calloc(1, sizeof(GLTtext));

	_GLT_ASSERT(text);

	if (!text)
		return GLT_NULL;

	glGenVertexArrays(1, &text->_vao);
	glGenBuffers(1, &text->_vbo);

	_GLT_ASSERT(text->_vao);
	_GLT_ASSERT(text->_vbo);

	if (!text->_vao || !text->_vbo)
	{
		gltDeleteText(text);
		return GLT_NULL;
	}

	glBindVertexArray(text->_vao);

	glBindBuffer(GL_ARRAY_BUFFER, text->_vbo);

	glEnableVertexAttribArray(_GLT_TEXT2D_POSITION_LOCATION);
	glVertexAttribPointer(_GLT_TEXT2D_POSITION_LOCATION, _GLT_TEXT2D_POSITION_SIZE, GL_FLOAT, GL_FALSE, (_GLT_TEXT2D_VERTEX_SIZE * sizeof(GLfloat)), (const void*)(_GLT_TEXT2D_POSITION_OFFSET * sizeof(GLfloat)));

	glEnableVertexAttribArray(_GLT_TEXT2D_TEXCOORD_LOCATION);
	glVertexAttribPointer(_GLT_TEXT2D_TEXCOORD_LOCATION, _GLT_TEXT2D_TEXCOORD_SIZE, GL_FLOAT, GL_FALSE, (_GLT_TEXT2D_VERTEX_SIZE * sizeof(GLfloat)), (const void*)(_GLT_TEXT2D_TEXCOORD_OFFSET * sizeof(GLfloat)));

	glBindVertexArray(0);

	return text;
}

GLT_API void gltDeleteText(GLTtext *text)
{
	if (!text)
		return;

	if (text->_vao)
	{
		glDeleteVertexArrays(1, &text->_vao);
		text->_vao = GLT_NULL_HANDLE;
	}

	if (text->_vbo)
	{
		glDeleteBuffers(1, &text->_vbo);
		text->_vbo = GLT_NULL_HANDLE;
	}

	if (text->_text)
		free(text->_text);

	if (text->_vertices)
		free(text->_vertices);

	free(text);
}

GLT_API GLboolean gltSetText(GLTtext *text, const char *string)
{
	if (!text)
		return GL_FALSE;

	int strLength = 0;

	if (string)
		strLength = strlen(string);

	if (strLength)
	{
		if (text->_text)
		{
			if (strcmp(string, text->_text) == 0)
				return GL_TRUE;

			free(text->_text);
			text->_text = GLT_NULL;
		}

		text->_text = (char*)malloc((strLength + 1) * sizeof(char));

		if (text->_text)
		{
			memcpy(text->_text, string, (strLength + 1) * sizeof(char));

			text->_textLength = strLength;
			text->_dirty = GL_TRUE;

			return GL_TRUE;
		}
	}
	else
	{
		if (text->_text)
		{
			free(text->_text);
			text->_text = GLT_NULL;
		}
		else
		{
			return GL_TRUE;
		}

		text->_textLength = 0;
		text->_dirty = GL_TRUE;

		return GL_TRUE;
	}

	return GL_FALSE;
}

/* vvv
[[maybe_unused]] GLT_API const char* gltGetText(GLTtext *text)
{
	if (text && text->_text)
		return text->_text;

	return "\0";
}
*/

GLT_API void gltViewport(GLsizei width, GLsizei height)
{
	_GLT_ASSERT(width > 0);
	_GLT_ASSERT(height > 0);

	const GLfloat left = 0.0f;
	const GLfloat right = (GLfloat)width;
	const GLfloat bottom = (GLfloat)height;
	const GLfloat top = 0.0f;
	const GLfloat zNear = -1.0f;
	const GLfloat zFar = 1.0f;

	const GLfloat projection[16] = {
		(2.0f / (right - left)), 0.0f, 0.0f, 0.0f,
		0.0f, (2.0f / (top - bottom)), 0.0f, 0.0f,
		0.0f, 0.0f, (-2.0f / (zFar - zNear)), 0.0f,

		-((right + left) / (right - left)),
		-((top + bottom) / (top - bottom)),
		-((zFar + zNear) / (zFar - zNear)),
		1.0f,
	};

	memcpy(_gltText2DProjectionMatrix, projection, 16 * sizeof(GLfloat));
}

GLT_API void gltBeginDraw()
{
	glUseProgram(_gltText2DShader);

	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, _gltText2DFontTexture);
}

GLT_API void gltEndDraw()
{
	glUseProgram(0); // vvv
}

#define _gltDrawText() \
	glUniformMatrix4fv(_gltText2DShaderMVPUniformLocation, 1, GL_FALSE, mvp); \
	\
	glBindVertexArray(text->_vao); \
	glDrawArrays(GL_TRIANGLES, 0, text->vertexCount);

/* vvv
[[maybe_unused]] GLT_API void gltDrawText(GLTtext *text, const GLfloat mvp[16])
{
	if (!text)
		return;

	if (text->_dirty)
		_gltUpdateBuffers(text);

	if (!text->vertexCount)
		return;

	_gltDrawText();
}
*/

GLT_API void gltDrawText2D(GLTtext *text, GLfloat x, GLfloat y, GLfloat scale)
{
	if (!text)
		return;

	if (text->_dirty)
		_gltUpdateBuffers(text);

	if (!text->vertexCount)
		return;

#ifndef GLT_MANUAL_VIEWPORT
	GLint viewportWidth, viewportHeight;
	_gltGetViewportSize(&viewportWidth, &viewportHeight);
	gltViewport(viewportWidth, viewportHeight);
#endif

	const GLfloat model[16] = {
		scale, 0.0f, 0.0f, 0.0f,
		0.0f, scale, 0.0f, 0.0f,
		0.0f, 0.0f, scale, 0.0f,
		x, y, 0.0f, 1.0f,
	};

	GLfloat mvp[16];
	_gltMat4Mult(_gltText2DProjectionMatrix, model, mvp);

	_gltDrawText();
}

GLT_API void gltDrawText2DAligned(GLTtext *text, GLfloat x, GLfloat y, GLfloat scale, int horizontalAlignment, int verticalAlignment)
{
	if (!text)
		return;

	if (text->_dirty)
		_gltUpdateBuffers(text);

	if (!text->vertexCount)
		return;

	if (horizontalAlignment == GLT_CENTER)
		x -= gltGetTextWidth(text, scale) * 0.5f;
	else if (horizontalAlignment == GLT_RIGHT)
		x -= gltGetTextWidth(text, scale);

	if (verticalAlignment == GLT_CENTER)
		y -= gltGetTextHeight(text, scale) * 0.5f;
	else if (verticalAlignment == GLT_RIGHT)
		y -= gltGetTextHeight(text, scale);

	gltDrawText2D(text, x, y, scale);
}

/* vvv
[[maybe_unused]] GLT_API void gltDrawText3D(GLTtext *text, GLfloat x, GLfloat y, GLfloat z, GLfloat scale, GLfloat view[16], GLfloat projection[16])
{
	if (!text)
		return;

	if (text->_dirty)
		_gltUpdateBuffers(text);

	if (!text->vertexCount)
		return;

	const GLfloat model[16] = {
		scale, 0.0f, 0.0f, 0.0f,
		0.0f, -scale, 0.0f, 0.0f,
		0.0f, 0.0f, scale, 0.0f,
		x, y + (GLfloat)_gltFontGlyphHeight * scale, z, 1.0f,
	};

	GLfloat mvp[16];
	GLfloat vp[16];

	_gltMat4Mult(projection, view, vp);
	_gltMat4Mult(vp, model, mvp);

	_gltDrawText();
}
*/

GLT_API void gltColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
{
	glUniform4f(_gltText2DShaderColorUniformLocation, r, g, b, a);
}

/* vvv
[[maybe_unused]] GLT_API void gltGetColor(GLfloat *r, GLfloat *g, GLfloat *b, GLfloat *a)
{
	GLfloat color[4];
	glGetUniformfv(_gltText2DShader, _gltText2DShaderColorUniformLocation, color);

	if (r) (*r) = color[0];
	if (g) (*g) = color[1];
	if (b) (*b) = color[2];
	if (a) (*a) = color[3];
}
*/

GLT_API GLfloat gltGetLineHeight(GLfloat scale)
{
	return (GLfloat)_gltFontGlyphHeight * scale;
}

GLT_API GLfloat gltGetTextWidth(const GLTtext *text, GLfloat scale)
{
	if (!text || !text->_text)
		return 0.0f;

	GLfloat maxWidth = 0.0f;
	GLfloat width = 0.0f;

	_GLTglyph glyph;

	char c;
	int i;
	for (i = 0; i < text->_textLength; i++)
	{
		c = text->_text[i];

		if ((c == '\n') || (c == '\r'))
		{
			if (width > maxWidth)
				maxWidth = width;

			width = 0.0f;

			continue;
		}

		if (!gltIsCharacterSupported(c))
		{
#ifdef GLT_UNKNOWN_CHARACTER
			c = GLT_UNKNOWN_CHARACTER;
			if (!gltIsCharacterSupported(c))
				continue;
#else
			continue;
#endif
		}

		glyph = _gltFontGlyphs2[c - _gltFontGlyphMinChar];

		width += (GLfloat)glyph.w;
	}

	if (width > maxWidth)
		maxWidth = width;

	return maxWidth * scale;
}

GLT_API GLfloat gltGetTextHeight(const GLTtext *text, GLfloat scale)
{
	if (!text || !text->_text)
		return 0.0f;

	return (GLfloat)(gltCountNewLines(text->_text) + 1) * gltGetLineHeight(scale);
}

GLT_API GLboolean gltIsCharacterSupported(const char c)
{
	if (c == '\t') return GL_TRUE;
	if (c == '\n') return GL_TRUE;
	if (c == '\r') return GL_TRUE;

	int i;
	for (i = 0; i < _gltFontGlyphCount; i++)
	{
		if (_gltFontGlyphCharacters[i] == c)
			return GL_TRUE;
	}

	return GL_FALSE;
}

/* vvv
[[maybe_unused]] GLT_API GLint gltCountSupportedCharacters(const char *str)
{
	if (!str)
		return 0;

	GLint count = 0;

	while ((*str) != '\0')
	{
		if (gltIsCharacterSupported(*str))
			count++;

		str++;
	}

	return count;
}
*/

GLT_API GLboolean gltIsCharacterDrawable(const char c)
{
	if (c < _gltFontGlyphMinChar) return GL_FALSE;
	if (c > _gltFontGlyphMaxChar) return GL_FALSE;

	if (_gltFontGlyphs2[c - _gltFontGlyphMinChar].drawable)
		return GL_TRUE;

	return GL_FALSE;
}

GLT_API GLint gltCountDrawableCharacters(const char *str)
{
	if (!str)
		return 0;

	GLint count = 0;

	while ((*str) != '\0')
	{
		if (gltIsCharacterDrawable(*str))
			count++;

		str++;
	}

	return count;
}

GLT_API GLint gltCountNewLines(const char *str)
{
	GLint count = 0;

	while ((str = strchr(str, '\n')) != NULL)
	{
		count++;
		str++;
	}

	return count;
}

/* vvv
[[maybe_unused]] GLT_API void _gltGetViewportSize(GLint *width, GLint *height)
{
	GLint dimensions[4];
	glGetIntegerv(GL_VIEWPORT, dimensions);

	if (width) (*width) = dimensions[2];
	if (height) (*height) = dimensions[3];
}
*/

GLT_API void _gltMat4Mult(const GLfloat lhs[16], const GLfloat rhs[16], GLfloat result[16])
{
	int c, r, i;

	for (c = 0; c < 4; c++)
	{
		for (r = 0; r < 4; r++)
		{
			result[_GLT_MAT4_INDEX(r, c)] = 0.0f;

			for (i = 0; i < 4; i++)
				result[_GLT_MAT4_INDEX(r, c)] += lhs[_GLT_MAT4_INDEX(r, i)] * rhs[_GLT_MAT4_INDEX(i, c)];
		}
	}
}

GLT_API void _gltUpdateBuffers(GLTtext *text)
{
	if (!text || !text->_dirty)
		return;

	if (text->_vertices)
	{
		text->vertexCount = 0;

		free(text->_vertices);
		text->_vertices = GLT_NULL;
	}

	if (!text->_text || !text->_textLength)
	{
		text->_dirty = GL_FALSE;
		return;
	}

	const GLsizei countDrawable = gltCountDrawableCharacters(text->_text);

	if (!countDrawable)
	{
		text->_dirty = GL_FALSE;
		return;
	}

	const GLsizei vertexCount = countDrawable * 2 * 3; // 3 vertices in a triangle, 2 triangles in a quad

	const GLsizei vertexSize = _GLT_TEXT2D_VERTEX_SIZE;
	GLfloat *vertices = (GLfloat*)malloc(vertexCount * vertexSize * sizeof(GLfloat));

	if (!vertices)
		return;

	GLsizei vertexElementIndex = 0;

	GLfloat glyphX = 0.0f;
	GLfloat glyphY = 0.0f;

	GLfloat glyphWidth;
	const GLfloat glyphHeight = (GLfloat)_gltFontGlyphHeight;

	const GLfloat glyphAdvanceX = 0.0f;
	const GLfloat glyphAdvanceY = 0.0f;

	_GLTglyph glyph;

	char c;
	int i;
	for (i = 0; i < text->_textLength; i++)
	{
		c = text->_text[i];

		if (c == '\n')
		{
			glyphX = 0.0f;
			glyphY += glyphHeight + glyphAdvanceY;

			continue;
		}
		else if (c == '\r')
		{
			glyphX = 0.0f;

			continue;
		}

		if (!gltIsCharacterSupported(c))
		{
#ifdef GLT_UNKNOWN_CHARACTER
			c = GLT_UNKNOWN_CHARACTER;
			if (!gltIsCharacterSupported(c))
				continue;
#else
			continue;
#endif
		}

		glyph = _gltFontGlyphs2[c - _gltFontGlyphMinChar];

		glyphWidth = (GLfloat)glyph.w;

		if (glyph.drawable)
		{
			vertices[vertexElementIndex++] = glyphX;
			vertices[vertexElementIndex++] = glyphY;
			vertices[vertexElementIndex++] = glyph.u1;
			vertices[vertexElementIndex++] = glyph.v1;

			vertices[vertexElementIndex++] = glyphX + glyphWidth;
			vertices[vertexElementIndex++] = glyphY + glyphHeight;
			vertices[vertexElementIndex++] = glyph.u2;
			vertices[vertexElementIndex++] = glyph.v2;

			vertices[vertexElementIndex++] = glyphX + glyphWidth;
			vertices[vertexElementIndex++] = glyphY;
			vertices[vertexElementIndex++] = glyph.u2;
			vertices[vertexElementIndex++] = glyph.v1;

			vertices[vertexElementIndex++] = glyphX;
			vertices[vertexElementIndex++] = glyphY;
			vertices[vertexElementIndex++] = glyph.u1;
			vertices[vertexElementIndex++] = glyph.v1;

			vertices[vertexElementIndex++] = glyphX;
			vertices[vertexElementIndex++] = glyphY + glyphHeight;
			vertices[vertexElementIndex++] = glyph.u1;
			vertices[vertexElementIndex++] = glyph.v2;

			vertices[vertexElementIndex++] = glyphX + glyphWidth;
			vertices[vertexElementIndex++] = glyphY + glyphHeight;
			vertices[vertexElementIndex++] = glyph.u2;
			vertices[vertexElementIndex++] = glyph.v2;
		}

		glyphX += glyphWidth + glyphAdvanceX;
	}

	text->vertexCount = vertexCount;
	text->_vertices = vertices;

	glBindBuffer(GL_ARRAY_BUFFER, text->_vbo);
	glBufferData(GL_ARRAY_BUFFER, vertexCount * _GLT_TEXT2D_VERTEX_SIZE * sizeof(GLfloat), vertices, GL_DYNAMIC_DRAW);

	text->_dirty = GL_FALSE;
}

GLT_API GLboolean gltInit(void)
{
	if (gltInitialized)
		return GL_TRUE;

	if (!_gltCreateText2DShader())
		return GL_FALSE;

	if (!_gltCreateText2DFontTexture())
		return GL_FALSE;

	gltInitialized = GL_TRUE;
	return GL_TRUE;
}

GLT_API void gltTerminate(void)
{
	if (_gltText2DShader != GLT_NULL_HANDLE)
	{
		glDeleteProgram(_gltText2DShader);
		_gltText2DShader = GLT_NULL_HANDLE;
	}

	if (_gltText2DFontTexture != GLT_NULL_HANDLE)
	{
		glDeleteTextures(1, &_gltText2DFontTexture);
		_gltText2DFontTexture = GLT_NULL_HANDLE;
	}

	gltInitialized = GL_FALSE;
}

static const GLchar* _gltText2DVertexShaderSource =
"#version 330 core\n"
"\n"
"in vec2 position;\n"
"in vec2 texCoord;\n"
"\n"
"uniform mat4 mvp;\n"
"\n"
"out vec2 fTexCoord;\n"
"\n"
"void main()\n"
"{\n"
"	fTexCoord = texCoord;\n"
"	\n"
"	gl_Position = mvp * vec4(position, 0.0, 1.0);\n"
"}\n";

static const GLchar* _gltText2DFragmentShaderSource =
"#version 330 core\n"
"\n"
"out vec4 fragColor;\n"
"\n"
"uniform sampler2D diffuse;\n"
"\n"
"uniform vec4 color = vec4(1.0, 1.0, 1.0, 1.0);\n"
"\n"
"in vec2 fTexCoord;\n"
"\n"
"void main()\n"
"{\n"
"	fragColor = texture(diffuse, fTexCoord) * color;\n"
"}\n";

GLT_API GLboolean _gltCreateText2DShader(void)
{
	GLuint vertexShader, fragmentShader;
	GLint compileStatus, linkStatus;

#ifdef GLT_DEBUG_PRINT
	GLint infoLogLength;
	GLsizei infoLogSize;
	GLchar *infoLog;
#endif

	vertexShader = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vertexShader, 1, &_gltText2DVertexShaderSource, NULL);
	glCompileShader(vertexShader);

	glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &compileStatus);

	if (compileStatus != GL_TRUE)
	{
#ifdef GLT_DEBUG_PRINT
		glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &infoLogLength);

		// If no information log exists 0 is returned or 1 for a
		// string only containing the null termination char.
		if (infoLogLength > 1)
		{
			infoLogSize = infoLogLength * sizeof(GLchar);
			infoLog = (GLchar*)malloc(infoLogSize);

			glGetShaderInfoLog(vertexShader, infoLogSize, NULL, infoLog);

			printf("Vertex Shader #%u <Info Log>:\n%s\n", vertexShader, infoLog);

			free(infoLog);
		}
#endif

		glDeleteShader(vertexShader);
		gltTerminate();

#ifdef GLT_DEBUG
		_GLT_ASSERT(compileStatus == GL_TRUE);
		return GL_FALSE;
#else
		return GL_FALSE;
#endif
	}

	fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fragmentShader, 1, &_gltText2DFragmentShaderSource, NULL);
	glCompileShader(fragmentShader);

	glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &compileStatus);

	if (compileStatus != GL_TRUE)
	{
#ifdef GLT_DEBUG_PRINT
		glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &infoLogLength);

		// If no information log exists 0 is returned or 1 for a
		// string only containing the null termination char.
		if (infoLogLength > 1)
		{
			infoLogSize = infoLogLength * sizeof(GLchar);
			infoLog = (GLchar*)malloc(infoLogSize);

			glGetShaderInfoLog(fragmentShader, infoLogSize, NULL, infoLog);

			printf("Fragment Shader #%u <Info Log>:\n%s\n", fragmentShader, infoLog);

			free(infoLog);
		}
#endif

		glDeleteShader(vertexShader);
		glDeleteShader(fragmentShader);
		gltTerminate();

#ifdef GLT_DEBUG
		_GLT_ASSERT(compileStatus == GL_TRUE);
		return GL_FALSE;
#else
		return GL_FALSE;
#endif
	}

	_gltText2DShader = glCreateProgram();

	glAttachShader(_gltText2DShader, vertexShader);
	glAttachShader(_gltText2DShader, fragmentShader);

	glBindAttribLocation(_gltText2DShader, _GLT_TEXT2D_POSITION_LOCATION, "position");
	glBindAttribLocation(_gltText2DShader, _GLT_TEXT2D_TEXCOORD_LOCATION, "texCoord");

	glBindFragDataLocation(_gltText2DShader, 0, "fragColor");

	glLinkProgram(_gltText2DShader);

	glDetachShader(_gltText2DShader, vertexShader);
	glDeleteShader(vertexShader);

	glDetachShader(_gltText2DShader, fragmentShader);
	glDeleteShader(fragmentShader);

	glGetProgramiv(_gltText2DShader, GL_LINK_STATUS, &linkStatus);

	if (linkStatus != GL_TRUE)
	{
#ifdef GLT_DEBUG_PRINT
		glGetProgramiv(_gltText2DShader, GL_INFO_LOG_LENGTH, &infoLogLength);

		// If no information log exists 0 is returned or 1 for a
		// string only containing the null termination char.
		if (infoLogLength > 1)
		{
			infoLogSize = infoLogLength * sizeof(GLchar);
			infoLog = (GLchar*)malloc(infoLogSize);

			glGetProgramInfoLog(_gltText2DShader, infoLogSize, NULL, infoLog);

			printf("Program #%u <Info Log>:\n%s\n", _gltText2DShader, infoLog);

			free(infoLog);
		}
#endif

		gltTerminate();

#ifdef GLT_DEBUG
		_GLT_ASSERT(linkStatus == GL_TRUE);
		return GL_FALSE;
#else
		return GL_FALSE;
#endif
	}

	glUseProgram(_gltText2DShader);

	_gltText2DShaderMVPUniformLocation = glGetUniformLocation(_gltText2DShader, "mvp");
	_gltText2DShaderColorUniformLocation = glGetUniformLocation(_gltText2DShader, "color");

	glUniform1i(glGetUniformLocation(_gltText2DShader, "diffuse"), 0);

	glUseProgram(0);

	return GL_TRUE;
}

static const uint64_t _gltFontGlyphRects[_gltFontGlyphCount] = {
	0x1100040000, 0x304090004, 0x30209000D, 0x304090016, 0x30209001F, 0x304090028, 0x302090031, 0x409003A,
	0x302090043, 0x30109004C, 0x1080055, 0x30209005D, 0x302090066, 0x3040A006F, 0x304090079, 0x304090082,
	0x409008B, 0x4090094, 0x30409009D, 0x3040900A6, 0x3020900AF, 0x3040900B8, 0x3040900C1, 0x3040A00CA,
	0x3040900D4, 0x40A00DD, 0x3040900E7, 0x3020900F0, 0x3020900F9, 0x302090102, 0x30209010B, 0x302090114,
	0x30209011D, 0x302090126, 0x30209012F, 0x302070138, 0x30209013F, 0x302090148, 0x302090151, 0x3020A015A,
	0x3020A0164, 0x30209016E, 0x302090177, 0x102090180, 0x302090189, 0x302090192, 0x30209019B, 0x3020901A4,
	0x3020901AD, 0x3020A01B6, 0x3020901C0, 0x3020901C9, 0x3020901D2, 0x3020901DB, 0x3020901E4, 0x3020901ED,
	0x3020901F6, 0x3020A01FF, 0x302090209, 0x302090212, 0x30209021B, 0x302090224, 0x30209022D, 0x309060236,
	0x10906023C, 0x302070242, 0x302090249, 0x706090252, 0x50409025B, 0x202090264, 0x10207026D, 0x102070274,
	0x30406027B, 0x104060281, 0x2010B0287, 0x3020A0292, 0xB0007029C, 0x5040A02AA, 0x3020A02B4, 0x6050902BE,
	0x20702C7, 0x20702CE, 0xB010902D5,
};

#define _GLT_FONT_GLYPH_DATA_TYPE uint64_t
#define _GLT_FONT_PIXEL_SIZE_BITS 2

#define _gltFontGlyphDataCount 387

static const _GLT_FONT_GLYPH_DATA_TYPE _gltFontGlyphData[_gltFontGlyphDataCount] = {
	0x551695416A901554, 0x569695A5A56AA55A, 0x555554155545AA9, 0x916AA41569005A40, 0xA5A569695A5A5696, 0x51555556AA569695, 0x696916A941554155, 0x69155A55569555A5,
	0x15541555456A9569, 0xA9569545A4005500, 0x569695A5A569695A, 0x5545AA9569695A5A, 0x916A941554555415, 0x55A56AA95A5A5696, 0x40555416A9555695, 0x55A45AA505550155,
	0xA55AAA455A555691, 0x169005A45569155, 0xA945554015400554, 0x569695A5A569695A, 0x9545AA9569695A5A, 0x4555555AA95A5556, 0x55A4016900154555, 0xA569695A5A45AA90,
	0x69695A5A569695A5, 0x9001541555455555, 0x5AA4155505A4016, 0xA40169005A501695, 0x155555AAA4569505, 0x5A405A4015405555, 0x5A505A545AA45554, 0x5A405A405A405A40,
	0x555556A95A555A40, 0x569005A400551554, 0x9569A569695A5A45, 0xA5A56969169A556A, 0xA405555555155555, 0x169005A50169505A, 0x9505A40169005A40, 0x5555155555AAA456,
	0x95A66916AA905555, 0x695A6695A6695A66, 0x154555555A5695A6, 0x5696916AA4155555, 0x9695A5A569695A5A, 0x45555155555A5A56, 0xA5A5696916A94155, 0x9569695A5A569695,
	0x155515541555456A, 0x695A5A5696916AA4, 0x56AA569695A5A569, 0x5540169155A55569, 0x56AA515550015400, 0x9695A5A569695A5A, 0x5A5516AA55A5A56, 0x500155005A401695,
	0xA56A695A5A455555, 0x169015A55569556, 0x54005500155005A4, 0x555A555695AA9455, 0xAA569555A5505AA5, 0x15415551555556, 0x5A55AAA455A50169, 0x40169005A4556915,
	0x550155505AA5055A, 0xA569695A5A455555, 0x69695A5A569695A5, 0x555554155545AA95, 0x5A5A569695A5A455, 0xA515AA55A5A56969, 0x1545505500555055, 0x95A6695A6695A569,
	0xA4569A55A6695A66, 0x5551555015554569, 0x456A9569695A5A45, 0xA5A5696916A95569, 0x4155545555155555, 0xA45A5A45A5A45A5A, 0xA945A5A45A5A45A5, 0x56A915A555695056,
	0x4555501554055551, 0x6945695169555AAA, 0x55AAA45569156955, 0x5A50055055551555, 0xA569695A5A45AA50, 0x69695A5A56AA95A5, 0x555555155555A5A5, 0x5A5A5696916AA415,
	0x5A5696956AA56969, 0x1555556AA569695A, 0x96916A9415541555, 0x9555A555695A5A56, 0x6A9569695A5A4556, 0xA405551554155545, 0x69695A5A45A6905A, 0x695A5A569695A5A5,
	0x5550555555AA55A, 0x5A555695AAA45555, 0x4556916AA4156955, 0x5555AAA45569155A, 0x95AAA45555555515, 0x6AA41569555A5556, 0xA40169155A455691, 0x1554005500155005,
	0x695A5A5696916A94, 0x5A5A56A69555A555, 0x54155545AA956969, 0x569695A5A4555555, 0x9695AAA569695A5A, 0x55A5A569695A5A56, 0x55AA455555551555, 0x5A416905A456915A,
	0x515555AA45A51690, 0x169005A400550055, 0x9555A40169005A40, 0x456A9569695A5A56, 0xA5A4555515541555, 0xA55A69569A569695, 0x6969169A45A6915A, 0x555555155555A5A5,
	0x5A40169005A400, 0x5A40169005A40169, 0x155555AAA4556900, 0x695A569154555555, 0x6695A6695A9A95A5, 0xA5695A5695A6695A, 0x55154555555A5695, 0x95A5695A56915455,
	0x695AA695A6A95A5A, 0x5695A5695A5695A9, 0x155455154555555A, 0x695A5A5696916A94, 0x5A5A569695A5A569, 0x541555456A956969, 0x5696916AA4155515, 0x56956AA569695A5A,
	0x5005A40169155A55, 0x6A94155400550015, 0xA569695A5A569691, 0x69695A5A569695A5, 0x5A5415A5456A95, 0x16AA415555500155, 0xAA569695A5A56969, 0x569695A5A55A6956,
	0x5545555155555A5A, 0x5555A5696916A941, 0xA5545A5005A5155A, 0x41555456A9569695, 0x56955AAA45555155, 0x9005A40169055A51, 0x5A40169005A4016, 0x5A45555055001550,
	0x569695A5A569695A, 0x9695A5A569695A5A, 0x515541555456A956, 0xA5A569695A5A4555, 0xA569695A5A569695, 0x555055A515AA55A5, 0x95A5691545505500, 0x695A6695A5695A56,
	0x9A4569A55A6695A6, 0x555015554169A456, 0x9569695A5A455551, 0x5A6515A515694566, 0x555A5A569695A5A4, 0x5A5A455555555155, 0xA9569695A5A56969, 0x169015A41569456,
	0x55505500155005A4, 0x5A55169555AAA45, 0x55A455A555A515A5, 0x5155555AAA455690, 0x696916A941554555, 0xA95A5A56A695A9A5, 0x56A9569695A6A569, 0x9401540155415554,
	0x5A5516AA45A9516, 0xA40169005A401695, 0x4154005540169005, 0xA5A5696916A94155, 0x9556945695169555, 0x55555AAA45569156, 0x6916A94155455551, 0x56A5169555A5A569,
	0xA9569695A5A56955, 0x15415541555456, 0x4169A4055A4005A4, 0xA916969169A5169A, 0x50056954569555AA, 0x5AAA455551540015, 0xAA41569555A55569, 0x55A555A551695516,
	0x55005550555555AA, 0x915694569416A401, 0xA5A569695A5A45AA, 0x41555456A9569695, 0x69555AAA45555155, 0x9415A415A5056951, 0x169015A40569056, 0xA941554015400554,
	0x569A95A5A5696916, 0x9695A5A56A6956A9, 0x415541555456A956, 0xA5A5696916A94155, 0x516AA55A5A569695, 0x155415A915694569, 0x555A95A915505540, 0x5A55A95A91555545,
	0x1694154154555569, 0xA456956A95AA56A9, 0x55416905A415515, 0x696916A941554154, 0x9055A515A555A5A5, 0x5A4016900554056, 0xAA45555055001550, 0x5505555155555A,
	0x6955AAA4569505A4, 0x5500155055A515, 0x690169405A400550, 0x90569415A415A505, 0x569015A415A5056, 0x6941540015400554, 0xA456915A55A55691, 0x16905A505A416905,
	0x6901555405541694, 0x16905A505A416941, 0x6955A45A516905A4, 0xA455415415545695, 0x6A45555515556A56, 0x56A45555515556A5, 0xA56A45555515556A, 0x5505515555A56956,
	0x690569A4016A5001, 0x4056954169A9459A, 0x416A690156941569, 0x15A9505A695169A6, 0x4015505540055540, 0x94169A4169A405A9, 0x5A56A9A4555A415A, 0x555169A955A5A55A,
	0x6945A90555555415, 0x1555154055416941, 0x56AAA456A545A690, 0x40555515A69156A5, 0x6945A69015550555, 0xA6915A6956AAA45A, 0x5A6956AAA45A6955, 0x455540555515A691,
	0xAA9555556AA91555, 0xA915555554555556, 0x416905A556955A56, 0x5A416905A416905A, 0x555515555AA45690, 0x6905A516955AA455, 0x416905A416905A41, 0x55556A95A556905A,
	0xA5A5696915555554, 0x5555155555,
};

GLT_API GLboolean _gltCreateText2DFontTexture(void)
{
	if (gltInitialized)
		return GL_TRUE;

	memset(_gltFontGlyphs, 0, _gltFontGlyphCount * sizeof(_GLTglyph));
	memset(_gltFontGlyphs2, 0, _gltFontGlyphLength * sizeof(_GLTglyph));

	GLsizei texWidth = 0;
	GLsizei texHeight = 0;

	GLsizei drawableGlyphCount = 0;

	_GLTglyphdata *glyphsData = (_GLTglyphdata*)calloc(_gltFontGlyphCount, sizeof(_GLTglyphdata));

	uint64_t glyphPacked;
	uint32_t glyphMarginPacked;

	uint16_t glyphX, glyphY, glyphWidth, glyphHeight;
	uint16_t glyphMarginLeft, glyphMarginTop, glyphMarginRight, glyphMarginBottom;

	uint16_t glyphDataWidth, glyphDataHeight;

	glyphMarginLeft = 0;
	glyphMarginRight = 0;

	_GLTglyph *glyph;
	_GLTglyphdata *glyphData;

	char c;
	int i;
	int x, y;

	for (i = 0; i < _gltFontGlyphCount; i++)
	{
		c = _gltFontGlyphCharacters[i];

		glyphPacked = _gltFontGlyphRects[i];

		glyphX = (glyphPacked >> (uint64_t)(8 * 0)) & 0xFFFF;
		glyphWidth = (glyphPacked >> (uint64_t)(8 * 2)) & 0xFF;

		glyphY = 0;
		glyphHeight = _gltFontGlyphHeight;

		glyphMarginPacked = (glyphPacked >> (uint64_t)(8 * 3)) & 0xFFFF;

		glyphMarginTop = (glyphMarginPacked >> (uint16_t)(0)) & 0xFF;
		glyphMarginBottom = (glyphMarginPacked >> (uint16_t)(8)) & 0xFF;

		glyphDataWidth = glyphWidth;
		glyphDataHeight = glyphHeight - (glyphMarginTop + glyphMarginBottom);

		glyph = &_gltFontGlyphs[i];

		glyph->c = c;

		glyph->x = glyphX;
		glyph->y = glyphY;
		glyph->w = glyphWidth;
		glyph->h = glyphHeight;

		glyphData = &glyphsData[i];

		glyphData->x = glyphX;
		glyphData->y = glyphY;
		glyphData->w = glyphWidth;
		glyphData->h = glyphHeight;

		glyphData->marginLeft = glyphMarginLeft;
		glyphData->marginTop = glyphMarginTop;
		glyphData->marginRight = glyphMarginRight;
		glyphData->marginBottom = glyphMarginBottom;

		glyphData->dataWidth = glyphDataWidth;
		glyphData->dataHeight = glyphDataHeight;

		glyph->drawable = GL_FALSE;

		if ((glyphDataWidth > 0) && (glyphDataHeight > 0))
			glyph->drawable = GL_TRUE;

		if (glyph->drawable)
		{
			drawableGlyphCount++;

			texWidth += (GLsizei)glyphWidth;

			if (texHeight < glyphHeight)
				texHeight = glyphHeight;
		}
	}

	const GLsizei textureGlyphPadding = 1; // amount of pixels added around the whole bitmap texture
	const GLsizei textureGlyphSpacing = 1; // amount of pixels added between each glyph on the final bitmap texture

	texWidth += textureGlyphSpacing * (drawableGlyphCount - 1);

	texWidth += textureGlyphPadding * 2;
	texHeight += textureGlyphPadding * 2;

	const GLsizei texAreaSize = texWidth * texHeight;

	const GLsizei texPixelComponents = 4; // R, G, B, A
	GLubyte *texData = (GLubyte*)malloc(texAreaSize * texPixelComponents * sizeof(GLubyte));

	GLsizei texPixelIndex;

	for (texPixelIndex = 0; texPixelIndex < (texAreaSize * texPixelComponents); texPixelIndex++)
		texData[texPixelIndex] = 0;

#define _GLT_TEX_PIXEL_INDEX(x, y) ((y) * texWidth * texPixelComponents + (x) * texPixelComponents)

#define _GLT_TEX_SET_PIXEL(x, y, r, g, b, a) { \
		texPixelIndex = _GLT_TEX_PIXEL_INDEX(x, y); \
		texData[texPixelIndex + 0] = r; \
		texData[texPixelIndex + 1] = g; \
		texData[texPixelIndex + 2] = b; \
		texData[texPixelIndex + 3] = a; \
	}

	const int glyphDataTypeSizeBits = sizeof(_GLT_FONT_GLYPH_DATA_TYPE) * 8; // 8 bits in a byte

	int data0Index = 0;
	int data1Index = 0;

	int bit0Index = 0;
	int bit1Index = 1;

	char c0 = '0';
	char c1 = '0';

	GLuint r = 0; // vvv
	GLuint g = 0; //
	GLuint b = 0; //
	GLuint a = 0; //

	GLfloat u1, v1;
	GLfloat u2, v2;

	GLsizei texX = 0;
	GLsizei texY = 0;

	texX += textureGlyphPadding;

	for (i = 0; i < _gltFontGlyphCount; i++)
	{
		glyph = &_gltFontGlyphs[i];
		glyphData = &glyphsData[i];

		if (!glyph->drawable)
			continue;

		c = glyph->c;

		glyphX = glyph->x;
		glyphY = glyph->y;
		glyphWidth = glyph->w;
		glyphHeight = glyph->h;

		glyphMarginLeft = glyphData->marginLeft;
		glyphMarginTop = glyphData->marginTop;
		glyphMarginRight = glyphData->marginRight;
		glyphMarginBottom = glyphData->marginBottom;

		glyphDataWidth = glyphData->dataWidth;
		glyphDataHeight = glyphData->dataHeight;

		texY = textureGlyphPadding;

		u1 = (GLfloat)texX / (GLfloat)texWidth;
		v1 = (GLfloat)texY / (GLfloat)texHeight;

		u2 = (GLfloat)glyphWidth / (GLfloat)texWidth;
		v2 = (GLfloat)glyphHeight / (GLfloat)texHeight;

		glyph->u1 = u1;
		glyph->v1 = v1;

		glyph->u2 = u1 + u2;
		glyph->v2 = v1 + v2;

		texX += glyphMarginLeft;
		texY += glyphMarginTop;

		for (y = 0; y < glyphDataHeight; y++)
		{
			for (x = 0; x < glyphDataWidth; x++)
			{
				c0 = (_gltFontGlyphData[data0Index] >> bit0Index) & 1;
				c1 = (_gltFontGlyphData[data1Index] >> bit1Index) & 1;

				if ((c0 == 0) && (c1 == 0))
				{
					r = 0;
					g = 0;
					b = 0;
					a = 0;
				}
				else if ((c0 == 0) && (c1 == 1))
				{
					r = 255;
					g = 255;
					b = 255;
					a = 255;
				}
				else if ((c0 == 1) && (c1 == 0))
				{
					r = 0;
					g = 0;
					b = 0;
					a = 255;
				}

				_GLT_TEX_SET_PIXEL(texX + x, texY + y, r, g, b, a);

				bit0Index += _GLT_FONT_PIXEL_SIZE_BITS;
				bit1Index += _GLT_FONT_PIXEL_SIZE_BITS;

				if (bit0Index >= glyphDataTypeSizeBits)
				{
					bit0Index = bit0Index % glyphDataTypeSizeBits;
					data0Index++;
				}

				if (bit1Index >= glyphDataTypeSizeBits)
				{
					bit1Index = bit1Index % glyphDataTypeSizeBits;
					data1Index++;
				}
			}
		}

		texX += glyphDataWidth;
		texY += glyphDataHeight;

		texX += glyphMarginRight;
		texX += textureGlyphSpacing;
	}

	for (i = 0; i < _gltFontGlyphCount; i++)
	{
		glyph = &_gltFontGlyphs[i];

		_gltFontGlyphs2[glyph->c - _gltFontGlyphMinChar] = *glyph;
	}

#undef _GLT_TEX_PIXEL_INDEX
#undef _GLT_TEX_SET_PIXEL

	glGenTextures(1, &_gltText2DFontTexture);
	glBindTexture(GL_TEXTURE_2D, _gltText2DFontTexture);

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

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

	free(texData);

	free(glyphsData);

	return GL_TRUE;
}

#endif

#ifdef __cplusplus
}
#endif

#endif
