
#include <ft2build.h>

#include FT_MODULE_H
#include FT_LCD_FILTER_H
#include FT_STROKER_H

#include <Spectre/System/Log.h>
#include "FreeTypeError.h"
#include "FreeTypeDriver.h"

class LibWrapper
{
public :
	static LibWrapper& getInstance()
	{
		static LibWrapper _inst;
		return _inst;
	};

	// Do not implement.
	LibWrapper(const LibWrapper&);
	void operator=(const LibWrapper&);

	FT_Library handle;

	FT_Stroker stroker;

private :

	LibWrapper();
	~LibWrapper();
};

LibWrapper::LibWrapper()
{
	FT_Error error = FT_Init_FreeType(&handle);
	if (error) {
		log("Could not initialize FreeType\n");
		return;
	} else {
		log("FreeType font driver was initialized.\n");
	}

	error = FT_Stroker_New(handle, &stroker);
}

LibWrapper::~LibWrapper()
{
	FT_Error error;

	FT_Stroker_Done(stroker);

	error = FT_Done_FreeType(handle);
	if (error) {
		log("Could not close FreeType\n");
	}
}

FreeTypeDriver::FreeTypeDriver() :
m_face	(NULL)
{
}

FreeTypeDriver::~FreeTypeDriver()
{
	if (m_face) {
		FT_Done_Face(m_face);
	}
}

bool FreeTypeDriver::setCharacterSize(unsigned int size)
{
	FT_Error error = FT_Set_Pixel_Sizes(m_face, 0, size);
	if (error) {
		log("FreeType: failed to set character size\n");
		return false;
	}
	return true;
}

bool FreeTypeDriver::loadFromFile(const std::string& filename) 
{
	FT_Face face;
	FT_Error error;

	error = FT_New_Face(LibWrapper::getInstance().handle, filename.c_str(), 0, &face);
	if (error) {
		log("FreeType: could not load file (%s): %s\n", 
			filename.c_str(), FT_GetErrorString(error));
		return false;
	}

	error = FT_Select_Charmap(face, FT_ENCODING_UNICODE);
	if (error) {
		log("FreeType: (%s) failed to set unicode charmap\n", filename.c_str());
		return false;
	}

	m_face = face;

	return true;
}

Font::Glyph FreeTypeDriver::loadGlyph(unsigned int codepoint, Image& img, unsigned int outlineSize)
{
	Font::Glyph glyph;
	FT_Glyph glyph_info;
	FT_Int32 flags = FT_LOAD_TARGET_NORMAL;

	if (outlineSize > 0) {
		flags |= FT_LOAD_NO_BITMAP;
	}

	if (m_hinting) {
		flags |= FT_LOAD_FORCE_AUTOHINT;
	} else {
		flags |= FT_LOAD_NO_AUTOHINT;
	}

	if (FT_Load_Char(m_face, codepoint, flags) != 0) {
		log("FreeType: failed to load glyph for character code '%c'\n", codepoint);
	}

	FT_Error error = FT_Get_Glyph(m_face->glyph, &glyph_info);
	if (!error) {

		FT_Glyph_Metrics& metrics = m_face->glyph->metrics;

		if (outlineSize) {
			FT_Stroker stroker = LibWrapper::getInstance().stroker;

			FT_Stroker_Set(stroker, outlineSize * 64,
				FT_STROKER_LINECAP_ROUND,
				FT_STROKER_LINEJOIN_ROUND,
				0);

			FT_Glyph_Stroke(&glyph_info, stroker, false);
		}

		if (metrics.width > 0 && metrics.height > 0) {

			FT_Glyph_To_Bitmap(&glyph_info, FT_RENDER_MODE_NORMAL, 0, true);
			FT_Bitmap& bmp = reinterpret_cast<FT_BitmapGlyph>(glyph_info)->bitmap;

			img.create(PixelFormat::PF_Alpha,
				bmp.width,
				bmp.rows,
				bmp.buffer);

			glyph.offset.x	= static_cast<unsigned char>(metrics.horiBearingX / (1 << 6));
			glyph.offset.y	= static_cast<unsigned char>(metrics.horiBearingY / (1 << 6));
			glyph.size.x	= static_cast<unsigned char>(metrics.width / (1 << 6)) + (outlineSize * 2);
			glyph.size.y	= static_cast<unsigned char>(metrics.height / (1 << 6)) + (outlineSize * 2);
		}

		glyph.advance = static_cast<unsigned char>(metrics.horiAdvance >> 6) + (outlineSize * 2);
	} else {
		log("FreeType: failed to load glyph for character code '%c'\n", codepoint);
	}

	return glyph;
}

std::string FreeTypeDriver::getName()
{
	return m_face->family_name;
}