#ifndef FREE_GLYPH_H
#define FREE_GLYPH_H

#include <stdbool.h>
#include <stdint.h>

#include <ft2build.h>
#include FT_FREETYPE_H

#include "vec2.h"
#include "vec4.h"
#include "glyph_attr.h"
#include "simple_render.h"

struct glyph_info {
	struct vec2i64 a;
	struct {
		uint32_t w;
		uint32_t h;
		int32_t l;
		int32_t t;
	} b;

	float tx;
};

struct free_glyph_atlas {
	uint32_t font_tex;
	struct vec2ui atlas_dim;

	struct glyph_info g_infos[128];
};

struct free_glyph_atlas fga_create(FT_Face font_face);

float fga_calc_col(const struct free_glyph_atlas *fga, const struct app *app,
		   const char *cstr, size_t cstr_size,
		   struct vec2f pos, size_t index);

float fga_calc_col_buffer(const struct free_glyph_atlas *fga,
			  const struct app *app);

void fga_calc(struct free_glyph_atlas *fga, struct simple_render *sr,
	      const struct app *app, const char *cstr, size_t cstr_size,
	      struct vec2f pos, struct vec4f fg_color, struct vec4f bg_color);
void fga_calc_buffer(struct free_glyph_atlas *fga, struct simple_render *sr,
		     const struct app *app, struct vec2f pos);

void fga_load_texture_atlas_or_exit(struct free_glyph_atlas *fga,
				    FT_Face font_face);

struct vec2ui fga_calc_atlas_dimensions(FT_Face font_face);

#if defined(FREE_GLYPH_IMP) || defined(IMP)

#include "shader.h"
#include "gl_util.h"

struct free_glyph_atlas
fga_create(FT_Face font_face)
{
	struct free_glyph_atlas fga = {0};
	fga_load_texture_atlas_or_exit(&fga, font_face);
	return fga;
}

float
fga_calc_col(const struct free_glyph_atlas *fga, const struct app *app,
	     const char *cstr, size_t cstr_size, struct vec2f pos,
	     size_t index)
{
	struct vec2f pen = pos;
	struct vec2f dim = vec2f_from_ui(fga->atlas_dim);
	if ( index > cstr_size ) {
		index = cstr_size - 1;
	}
	for ( size_t i = 0; i < index; ++i ) {
		char c = cstr[i];
		if ( c == '\n' ) {
			pen.x = 0;
			pen.y -= dim.y;
			continue;
		}
		struct glyph_info gi = fga->g_infos[(int64_t)c];
		if ( c == '\t' ) {
			c = ' ';
			gi = fga->g_infos[(int64_t)c];
			gi.a.x *= app->cfg.tab_size;
		}

		pen.x += (float) gi.a.x;
		pen.y += (float) gi.a.y;
	}
	return pen.x;
}

float
fga_calc_col_buffer(const struct free_glyph_atlas *fga,
		    const struct app *app)
{
	return fga_calc_col(fga, app, app->buf.data.items,
					  app->buf.data.size, vec2fs(0),
					  app->buf.cur);
}

void
fga_calc(struct free_glyph_atlas *fga, struct simple_render *sr,
	 const struct app *app, const char *cstr, size_t cstr_size,
	 struct vec2f pos, struct vec4f fg_color, struct vec4f bg_color)
{
	(void) sr;
	(void) fg_color;
	(void) bg_color;
	struct vec2f pen = pos;
	struct vec2f dim = vec2f_from_ui(fga->atlas_dim);
	for ( size_t i = 0; i < cstr_size; ++i ) {
		char c = cstr[i];
		if ( c == '\n' ) {
			pen.x = 0;
			pen.y -= dim.y;
			continue;
		}
		struct glyph_info gi = fga->g_infos[(int64_t)c];
		if ( c == '\t' ) {
			c = ' ';
			gi = fga->g_infos[(int64_t)c];
			gi.a.x *= app->cfg.tab_size;
		}

		float w = (float)gi.b.w;
		float h = (float)gi.b.h;

		sr_image_rect(sr,
			      vec2f(pen.x + (float) gi.b.l,
				    pen.y + (float) gi.b.t), /* pos */
			      vec2f(w, -h),	      /* size */
			      vec2f(gi.tx, 0.f),      /* uv_pos */
			      vec2f(w/dim.x, h/dim.y) /* uv_size */
			);

		pen.x += (float) gi.a.x;
		pen.y += (float) gi.a.y;
	}
}

void
fga_calc_buffer(struct free_glyph_atlas *fga, struct simple_render *sr,
		const struct app *app, struct vec2f size)
{
	fga_calc(fga, sr, app, app->buf.data.items, app->buf.data.size,
		 size, vec4fs(1), vec4fs(0));
}

void
fga_load_texture_atlas_or_exit(struct free_glyph_atlas *fga,
			       FT_Face font_face)
{
	struct vec2ui dim = fga_calc_atlas_dimensions(font_face);
	fga->atlas_dim = dim;

	glActiveTexture(GL_TEXTURE0);
	glGenTextures(1, &fga->font_tex);
	glBindTexture(GL_TEXTURE_2D, fga->font_tex);

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

	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, (int32_t)dim.x, (int32_t)dim.y,
		     0, GL_RED, GL_UNSIGNED_BYTE, 0);

	FT_GlyphSlot g = font_face->glyph;
	uint64_t x = 0;
	for ( uint64_t i = 0; i < 128; ++i ) {
		if ( FT_Load_Char(font_face, i, FT_LOAD_RENDER) ) {
			fprintf(stderr, "[ERROR] Failed to load char: %c\n",
				(char)i);
			exit(EXIT_FAILURE);
		}
		if ( FT_Render_Glyph(g, FT_RENDER_MODE_NORMAL) ) {
			fprintf(stderr, "[ERROR] Failed to render char: %c\n",
				(char)i);
			exit(EXIT_FAILURE);
		}

		glTexSubImage2D(GL_TEXTURE_2D, 0, (int32_t) x, 0,
				(int32_t) g->bitmap.width,
				(int32_t) g->bitmap.rows,
				GL_RED, GL_UNSIGNED_BYTE,
				g->bitmap.buffer);

		fga->g_infos[i] = (struct glyph_info) {
			.a = {
				.x = g->advance.x >> 6,
				.y = g->advance.y >> 6,
			},
			.b = {
				.w = g->bitmap.width,
				.h = g->bitmap.rows,
				.l = g->bitmap_left,
				.t = g->bitmap_top,
			},
			.tx = ((float) x) / ((float) dim.x),
		};

		x += g->bitmap.width;
	}
}

struct vec2ui
fga_calc_atlas_dimensions(FT_Face font_face)
{
	struct vec2ui dim = {0};

	for ( uint64_t i = 0; i < 128; ++i ) {
		if ( FT_Load_Char(font_face, i, FT_LOAD_RENDER) ) {
			fprintf(stderr, "[WARNING] Failed to load char: %c\n",
				(char)i);
			continue;
		}

		dim.x += font_face->glyph->bitmap.width;
		uint32_t row = font_face->glyph->bitmap.rows;
		dim.y = ( dim.y < row ) * row
			+ ( ! (dim.y < row) ) * dim.y;
	}

	return dim;
}


#endif /* defined(FREE_GLYPH_IMP) || defined(IMP) */

#endif
