/*
 * DESCRIPTION:
 * Text component.
*/

#include "component_text.h"

static void _component_text_glyph_size_get(ComponentText* self, Texture texture, vec2 size);
static void _component_text_free(ComponentText* self);
static void _component_text_glyph_draw(ComponentText* self, ECS* ecs, Texture texture, vec3 position);

/* Obtains the size of the glyph. */
static void
_component_text_glyph_size_get(ComponentText* self, Texture texture, vec2 size)
{
	size[0] = texture.size[0] * self->scale[0];
	size[1] = texture.size[1] * self->scale[1];
}

/* Frees glyph textures. */
static void
_component_text_free(ComponentText* self)
{
	for (s32 i = 0; i < (s32)self->length; i++)
	{
		Texture* texture;

		texture = &self->glyphTextures[i];

		if (texture->isInit)
			texture_free(texture);
	}

	memset(&self->glyphTextures, '\0', sizeof(self->glyphTextures));
}

/* Draws a glyph. */
static void
_component_text_glyph_draw(ComponentText* self, ECS* ecs, Texture texture, vec3 position)
{
	Atlas atlas;
	mat4 model;
	vec2 size;

	_component_text_glyph_size_get(self, texture, size);

	glm_mat4_identity(model);
	glm_translate(model, position);

	atlas_init(&atlas, texture, texture.size, (s32*)ATLAS_SIZE_DEFAULT);

	texture_quad_draw
	(
	 	&ecs->game->renderer,
		&ecs->game->resources.shaders[SHADER_TEXTURE_QUAD], 
		&atlas, 
		model, 
		size, 
		self->color
	);
}

/* Gets a rectangle the size of all glyphs. */
void
component_text_rectangle_get(ComponentText* self, Rectangle* rectangle)
{
	char glyph;
	char nextGlyph;
	f32 lineW;
	f32 offsetX;
	f32 prevY;
	GlyphMetrics glyphMetrics;
	vec2 origin;
	vec2 size;
	f32 lineSkip;
	f32 kerning;
	f32 advance;

	lineSkip = font_line_skip_get(self->font) * self->scale[1];

	glyph_metrics_get(&glyphMetrics, self->font, self->string[0]);

	lineW = 0.0f;

	size[0] = 0.0f;
	size[1] = (f32)glyphMetrics.maxY - glyphMetrics.minY;

	for (s32 i = 0; i < (s32)self->length; i++)
	{
		glyph = self->string[i];

		if 
		(
			glyph == '\n' ||
			(glyph == ' ' && (lineW > self->wrap) && self->wrap != -1)
		)
		{
			size[1] += lineSkip;
			lineW = 0.0f;

			if (glyph == '\n')
				continue;
		}

		if (i < (s32)self->length)
		{
			nextGlyph = self->string[i + 1];
			kerning = (f32)glyph_kerning_get(self->font, glyph, nextGlyph);
		}
		else
			kerning = 0.0;

		glyph_metrics_get(&glyphMetrics, self->font, glyph);

		advance = glyphMetrics.advance;

		offsetX = advance + kerning;
		offsetX *= self->scale[0];
	
		lineW += offsetX;

		if (lineW > size[0])
			size[0] = lineW;
	}

	rectangle->w = size[0];
	rectangle->h = size[1];
	rectangle->x = self->position[0] + self->offset[0];
	rectangle->y = self->position[1] + self->offset[1];
}

/* Given a string and its length, generates textures for the string. */
void
component_text_string_set(ComponentText* self, char* string, u32 length)
{
	_component_text_free(self);

	if (length > COMPONENT_TEXT_STRING_MAX)
		return;

	self->length = length;

	memset(self->string, '\0', sizeof(char) * COMPONENT_TEXT_STRING_MAX);
	memcpy(self->string, string, sizeof(char) * self->length);

	for (s32 i = 0; i < (s32)self->length; i++)
		glyph_texture_init(&self->glyphTextures[i], self->font, (char)string[i]);
}

/* Adds text component. */
void
component_text_add(ComponentText* self, ECS* ecs)
{
	glm_vec4_copy(COMPONENT_TEXT_COLOR, self->color);
	glm_vec2_copy((f32*)COMPONENT_TEXT_SCALE, self->scale);
}

/* Deletes text component. */
void
component_text_delete(ComponentText* self, ECS* ecs)
{
	_component_text_free(self);
}

/* Draws a text component. */
void
component_text_draw(ComponentText* self, ECS* ecs)
{
	char glyph;
	char nextGlyph;
	f32 advance;
	f32 lineW;
	f32 offsetX;
	f32 kerning;
	GlyphMetrics glyphMetrics;
	Rectangle rectangle;
	vec2 origin;
	vec3 position;
	f32 lineSkip;
	f32 lineBegin;

	component_text_rectangle_get(self, &rectangle);

	lineSkip = font_line_skip_get(self->font) * self->scale[1];

	glm_vec3_zero(position);

	position[0] = rectangle.x;
	position[1] = rectangle.y;

	position[0] += self->offset[0];
	position[1] += self->offset[1];
	position[2] = self->position[2] + self->offset[2];

	offsetX = 0.0f;
	lineW = 0.0f;

	lineBegin = position[0];

	for (s32 i = 0; i < (s32)self->length; i++)
	{
		glyph = self->string[i];
	
		if 
		(
			glyph == '\n' ||
			(glyph == ' ' && (lineW > self->wrap) && self->wrap != -1)
		)
		{
			position[0] = lineBegin;
			position[1] += lineSkip;

			lineW = 0.0f;

			if (glyph == '\n')
				continue;
		}

		if (i < (s32)self->length)
		{
			nextGlyph = self->string[i + 1];
			kerning = (f32)glyph_kerning_get(self->font, glyph, nextGlyph);
		}
		else
			kerning = 0.0;

		glyph_metrics_get(&glyphMetrics, self->font, glyph);

		_component_text_glyph_draw(self, ecs, self->glyphTextures[i], position);

		advance = glyphMetrics.advance;

		offsetX = advance + kerning;
		offsetX *= self->scale[0];

		position[0] += offsetX;
		lineW += offsetX;
	}
}

/* Sets text component info. */
void
component_text_init
(
	ComponentText* self, 
	Font* font,
	char* string,
	u32 length,
	s32 wrap,
	vec3 position,
	vec4 color
)
{
	self->font = font;
	self->wrap = wrap;

	glm_vec3_copy(position, self->position);
	glm_vec4_copy(color, self->color);

	component_text_string_set(self, string, length);
}
