#include "stdafx.h"

#include "wx/wx.h"

#include "font.h"
#include "freetype/ttunpat.h"
#include <math.h>

#include "freetypedll.h"

static char err[255]; // not exactly thread-safe

FontRenderer::FontRenderer(const char* filename0, const char* filename1, double ptsize, bool unpatented_hinting, bool hinting)
{
	char* err_str = SelectDLL(hinting ? 0 : 1);
	if (err_str)
	{
		sprintf(err, "Error loading FreeType DLL (freetype(a|b).dll (%d)): %s", hinting, err_str);
		throw err;
	}

	int error;
	
	error = DLLFT_Init_FreeType(&FontLibrary0);
	if (error)
	{
		throw "Error initialising FreeType";
	}

	error = DLLFT_Init_FreeType(&FontLibrary1);
	if (error)
	{
		DLLFT_Done_FreeType(FontLibrary0);
		throw "Error initialising FreeType";
	}

	FT_Parameter openparam = { FT_PARAM_TAG_UNPATENTED_HINTING, NULL };

	FT_Open_Args args0 = {
		FT_OPEN_PATHNAME | (unpatented_hinting ? FT_OPEN_PARAMS : 0),
		NULL, NULL,
		(FT_String*)filename0,
		NULL, NULL,
		1, &openparam
	};
	FT_Open_Args args1 = {
		FT_OPEN_PATHNAME | (unpatented_hinting ? FT_OPEN_PARAMS : 0),
		NULL, NULL,
		(FT_String*)filename1,
		NULL, NULL,
		1, &openparam
	};

	error = DLLFT_Open_Face(
		FontLibrary0,
		&args0,
		0, // index of face inside font file
		&FontFace0
	);
	if (error)
	{
		DLLFT_Done_FreeType(FontLibrary0);
		DLLFT_Done_FreeType(FontLibrary1);
		throw "Error loading primary font";
	}

	error = DLLFT_Open_Face(
		FontLibrary1,
		&args1,
		0, // index of face inside font file
		&FontFace1
	);
	if (error)
	{
		DLLFT_Done_Face(FontFace0);
		DLLFT_Done_FreeType(FontLibrary0);
		DLLFT_Done_FreeType(FontLibrary1);
		throw "Error loading secondary font face";
	}

	error = DLLFT_Set_Char_Size(
		FontFace0,
		0,			// char_width in 1/64th of points
		(int)(ptsize*64.0 + 0.5),	// char_height in 1/64th of points
		96,	// horizontal device resolution
		96	// vertical device resolution
	);
	if (error)
	{
		DLLFT_Done_Face(FontFace0);
		DLLFT_Done_Face(FontFace1);
		DLLFT_Done_FreeType(FontLibrary0);
		DLLFT_Done_FreeType(FontLibrary1);
		throw "Error loading scalable character from primary font - is this a TrueType font?";
	}

	error = DLLFT_Set_Char_Size(
		FontFace1,
		0,			// char_width in 1/64th of points
		(int)(ptsize*64.0 + 0.5),	// char_height in 1/64th of points
		96,	// horizontal device resolution
		96	// vertical device resolution
	);
	if (error)
	{
		DLLFT_Done_Face(FontFace0);
		DLLFT_Done_Face(FontFace1);
		DLLFT_Done_FreeType(FontLibrary0);
		DLLFT_Done_FreeType(FontLibrary1);
		throw "Error loading scalable character from secondary font - is this a TrueType font?";
	}

	Boldness = 0;
	Italicness = 0;
	Tracking = 0;
	Leading = 0;
}

FontRenderer::~FontRenderer()
{
	DLLFT_Done_Face(FontFace0);
	DLLFT_Done_Face(FontFace1);
	DLLFT_Done_FreeType(FontLibrary0);
	DLLFT_Done_FreeType(FontLibrary1);
}

#define deg2rad(a) ((double)a * 3.14159265358979323846 / 180.0)

int FontRenderer::LoadGlyph(const int charcode)
{
	FT_Face FontFace = FontFace0;
	int glyph_index = DLLFT_Get_Char_Index(FontFace0, charcode);

	Missing = false;
	if (glyph_index == 0)
	{
		// Can't find glyph in primary font - switch to secondary
		FontFace = FontFace1;
		glyph_index = DLLFT_Get_Char_Index(FontFace1, charcode);

		// Still can't find it - use the missing glyph symbol
		if (glyph_index == 0)
		{
			glyph_index = DLLFT_Get_Char_Index(FontFace1, 0xFFFD);
			Missing = true;
		}
	}


	int error;

	if (Italicness)
	{
		// Start with identity matrix  (rotations could be added in here easily)
		FT_Matrix mat;
		mat.xx = mat.yy = (FT_Fixed)(0x10000L);
		mat.xy = mat.yx = (FT_Fixed)(0x00000L);
		// Apply shear
		FT_Fixed f = (FT_Fixed)(tan(deg2rad(Italicness)) * 0x10000L);
		mat.xy += DLLFT_MulFix(f, mat.xx);
		mat.yy += DLLFT_MulFix(f, mat.yx);

		DLLFT_Set_Transform(FontFace, &mat, 0);
	}

	error = DLLFT_Load_Glyph(FontFace, glyph_index, FT_LOAD_DEFAULT);
	if (error)
		throw "Error loading glyph";


	if (Outline)
	{
		wxLogFatalError(wxT("Can't do this yet"));
		/*
		// Outline renderer which doesn't actually work:

		assert(FontFace->glyph->format == FT_GLYPH_FORMAT_OUTLINE);

		//FT_Outline *FontOutline = new FT_Outline;
		//error = FT_Outline_New(FontLibrary, 1024, 1024, FontOutline);
		//assert(!error);
		FT_Outline *FontOutline = &FontFace->glyph->outline;

		FT_BBox BBox;

		error = FT_Outline_Get_BBox(FontOutline, &BBox);
		assert(!error);

		// Translate back to (0,0)
		FT_Outline_Translate(FontOutline, -BBox.xMin, -BBox.yMin);

		FT_Bitmap *Bmp = new FT_Bitmap;
		Bmp->pixel_mode = FT_PIXEL_MODE_GRAY;
		Bmp->num_grays = 256;
		FontFace->glyph->bitmap_left = (BBox.xMin) >> 6;
		FontFace->glyph->bitmap_top = (BBox.yMax + 31) >> 6;
		Bmp->width = (BBox.xMax - BBox.xMin) >> 6;
		Bmp->pitch = 3*Bmp->width;
		Bmp->rows = (BBox.yMax - BBox.yMin) >> 6;
		Bmp->buffer = new unsigned char[Bmp->pitch * Bmp->rows];
		memset(Bmp->buffer, 0, sizeof(unsigned char) * Bmp->pitch * Bmp->rows);

		error = FT_Outline_Get_Bitmap(FontLibrary, FontOutline, Bmp);
		assert(!error);

		FontFace->glyph->bitmap = *Bmp;

		error = FT_Outline_Done(FontLibrary, FontOutline);
		assert(!error);
		*/
	}
	else
	{
		// Simple version:

		error = DLLFT_Render_Glyph(FontFace->glyph, FT_RENDER_MODE_NORMAL);
		if (error)
			throw "Error rendering glyph";
	}

	LastFontFace = FontFace;

	return glyph_index;
}


void FontRenderer::GetBoundingBox(int& width, int& height)
{
	FT_Bitmap* bmp = &LastFontFace->glyph->bitmap;
	width = bmp->width + Boldness;
	height = bmp->rows;
}


void FontRenderer::RenderGlyph(unsigned char* rgb_buffer, int& pos_left, int& pos_top, int width, int height, int pitch, bool single_glyph)
{
	FT_Face FontFace = LastFontFace;

	// This function only tries to work on 8-bit greyscale glyphs
	if (FontFace->glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
		throw "Error - invalid bitmap format";

	// Add desired spacing, but not after zero-width combining glyphs
	if (!single_glyph && FontFace->glyph->advance.x)
		pos_left += Tracking;

	FT_Bitmap* bmp = &FontFace->glyph->bitmap;

	// Pointers to the appropriate row of pixel data
	unsigned char* ImageRow = rgb_buffer;
	unsigned char* GlyphRow = bmp->buffer;

	int Left = pos_left;
	int Top = pos_top;
	if (!single_glyph)
	{
		Left += FontFace->glyph->bitmap_left;
		Top -= FontFace->glyph->bitmap_top;
	}

	// Point to the first line that needs to be drawn
	if (Top >= 0)
		ImageRow += Top*pitch;

	for (int GlyphY = 0; GlyphY < bmp->rows; ++GlyphY)
	{
		// Clip vertically
		if (Top+GlyphY < 0 || Top+GlyphY >= height)
		{
			GlyphRow += bmp->pitch;
			continue;
		}


		for (int GlyphX = 0; GlyphX < bmp->width+Boldness; ++GlyphX)
		{
			// Clip horizontally
			if (Left+GlyphX < 0 || Left+GlyphX >= width)
				continue;

			int value = 0;

			if (! Boldness)
			{
				value = GlyphRow[GlyphX];
			}
			else
			{
				// Glyph: 001122221100
				//
				// Calc:  001122221100
				//      +  001122221100
				//      = 0012344432100
				//        ||__     __||
				//   row[0]+row[1] row[10]+row[11]
				//      __|           |__
				//     row[0]       row[11]
				//
				// output[x] = sum row[x-boldness .. x]


				int min = GlyphX - Boldness; // inclusive
				if (min < 0) min = 0;
				int max = GlyphX+1; // not inclusive
				if (max > bmp->width) max = bmp->width;

				for (int x = min; x < max; ++x)
					value += GlyphRow[x];

			}

			// Additive composition of pixel onto image buffer
			value += ImageRow[(Left+GlyphX)*3];
			ImageRow[(Left+GlyphX)*3  ] =
			ImageRow[(Left+GlyphX)*3+1] =
			ImageRow[(Left+GlyphX)*3+2] = (unsigned char)(value > 255 ? 255 : value);

		}

		// Point everything at the next line
		ImageRow += pitch;
		GlyphRow += bmp->pitch;
	}

	if (!single_glyph)
	{
		// Advance the drawing-cursor ( >>6 due to 26.6-bit number format)
		pos_left += FontFace->glyph->advance.x >> 6;
		pos_top += FontFace->glyph->advance.y >> 6;
	}
}

int FontRenderer::GetLineSpacing()
{
	return Leading + (FontFace0->size->metrics.height >> 6);
}

void FontRenderer::GetMetrics(int& offset_x, int& offset_y, int& advance)
{
	offset_x = LastFontFace->glyph->bitmap_left;
	offset_y = LastFontFace->glyph->bitmap_top;
	advance = LastFontFace->glyph->advance.x >> 6;
	if (advance)
		offset_x += Tracking;
}

int FontRenderer::GetKerning(int left, int right)
{
	FT_Vector vec;
	DLLFT_Get_Kerning(LastFontFace, left, right, FT_KERNING_DEFAULT, &vec);
	assert(vec.y == 0);
	return vec.x>>6;
}