#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_BITMAP_H
#include FT_STROKER_H

#include "pistachio.h"

#define FLOAT_FROM_16_16(n) ((float)((n) >> 16) + (float)((n) & 0xffff) / 65536.0)

#define GAP_FACTOR  0.3
#define SLANT       0.25

#define POOL_SIZE 1024 * 1024

static Arena arena = {0};

FT_Library library;
FT_Face face;
FT_Stroker stroker;

int glyph_indexof(char c) {
	return (c < MIN_CHAR || c > MAX_CHAR) ? -1 : c - MIN_CHAR;
}

bool open_font(char *font_path) {
	if (FT_Init_FreeType(&library) != 0) {
		fprintf(stderr, "Could not initialise libfreetype\n");
		return false;
	}

	if (FT_New_Face(library, font_path, 0, &face) != 0) {
		fprintf(stderr, "Error loading font \"%s\"\n", font_path);
		return false;
	}

	FT_Stroker_New(library, &stroker);

	return true;
}

bool render_font(Screen_Info *info, Font_Attrs *attrs, u32 background, Glyph *chars) {
	if (!arena.initialized)
		make_arena(POOL_SIZE, &arena);

	ARGB fore, back;
	make_argb(attrs->color, &fore);
	make_argb(background, &back);

	float gap = 0;
	FT_Matrix matrix;

	if (attrs->oblique) {
		gap = SLANT * GAP_FACTOR * attrs->size;
		matrix = (FT_Matrix) { .xx = 0x10000, .xy = (int)(SLANT * 0x10000), .yx = 0, .yy = 0x10000 };
		FT_Set_Transform(face, &matrix, NULL);
	}
	if (attrs->bold)
		FT_Stroker_Set(stroker, 32, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);

	FT_Set_Char_Size(face, 0, attrs->size * 64, info->dpi_w, info->dpi_h);

	for (int c = MIN_CHAR; c <= MAX_CHAR; c++) {
		FT_Bitmap bmp;
		FT_Glyph glyph;
		int left, top;

		if (attrs->bold) {
			FT_Load_Char(face, c, FT_LOAD_NO_BITMAP);
			FT_Get_Glyph(face->glyph, &glyph);

			FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
			FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, NULL, 1);
			FT_BitmapGlyph bg = (FT_BitmapGlyph)glyph;

			bmp = bg->bitmap;
			left = bg->left;
			top = bg->top;
		}
		else {
			FT_Load_Char(face, c, FT_LOAD_RENDER);

			bmp = face->glyph->bitmap;
			left = face->glyph->bitmap_left;
			top = face->glyph->bitmap_top;
		}

		Glyph *gl = &chars[c - MIN_CHAR];
		*gl = (Glyph) {
			.img_w = bmp.width,
			.img_h = bmp.rows,
			.pitch = bmp.width * 4,
			.box_w = gap + FLOAT_FROM_16_16(face->glyph->linearHoriAdvance),
			.box_h = FLOAT_FROM_16_16(face->glyph->linearVertAdvance),
			.left  = left,
			.top   = top
		};

		int size = gl->pitch * gl->img_h;
		if (!size)
			continue;

		gl->data = allocate(&arena, size);
		if (!gl->data) {
			fprintf(stderr, "Failed to allocate glyph bitmap '%c'\n", c);
			return false;
		}

		u32 *p = (u32*)gl->data;
		for (int i = 0; i < gl->img_w * gl->img_h; i++) {
			float lum = (float)bmp.buffer[i] / 255.0;
			p[i] =
				((u32)(back.a + (fore.a - back.a) * lum) << 24) |
				((u32)(back.r + (fore.r - back.r) * lum) << 16) |
				((u32)(back.g + (fore.g - back.g) * lum) << 8) |
				(u32)(back.b + (fore.b - back.b) * lum);
		}

		if (attrs->bold)
			FT_Done_Glyph(glyph);
	}

	FT_Set_Transform(face, NULL, NULL);
	return true;
}

void close_font() {
	FT_Stroker_Done(stroker);
	FT_Done_Face(face);
	FT_Done_FreeType(library);
}