#include "FontManager.h"
#include "RenderDevice.h"

// project includes
#include "Utilities/Buffer.h"
#include "Utilities/Log.h"
#include "Utilities/format.h"

// library includes
#include "physfs.h"
#include "FTGLTextureFont.h"

// system includes
#include <iostream>

using namespace std;

#include "FontManagerDefaultFont.h"

namespace BlueCore
{

//------------------------------------------------------------------------------
FontManager::FontManager(RenderDevice* device) :
	_Device(device)
{
	// create default font
	FTFont *ftfont = new FTGLTextureFont (
			DefaultFontBytes,
			sizeof ( DefaultFontBytes ),
			false );
	ftfont->FaceSize( 16);
	_DefaultFont = new Font( ftfont, 0, device );

	if (_Device.valid())
		_Device->DeviceShutdownSignal.connect(this,
				&FontManager::DeviceShutdownSlot);

	clog << ">>> FontManager constructed..."<< endline;
}

//------------------------------------------------------------------------------
FontManager::~FontManager()
{
	destroy();

	clog << ">>> FontManager destructed..."<< endline;
}

//------------------------------------------------------------------------------
void FontManager::DeviceShutdownSlot()
{
	destroy();
}

//------------------------------------------------------------------------------
void FontManager::destroy()
{
	FontMap::iterator i;

	for (i = _fonts.begin(); i != _fonts.end(); i++)
	{
		if (i->second.valid())
			i->second->destroy();
	}

	_DefaultFont = 0;
}

//------------------------------------------------------------------------------
Font *FontManager::loadFont(const std::string &filename, int size, bool hinting)
{
	string key = format("%s-%d", filename.c_str(), size);

	// check if this font is already loaded
	FontMap::const_iterator result;
	result = _fonts.find(key);

	if (result != _fonts.end() && result->second.valid())
	{
		return result->second.pointer();
	}

	Font *font;

	PHYSFS_file *file = PHYSFS_openRead(filename.c_str() );

	if (file)
	{
		unsigned int file_size = PHYSFS_fileLength(file);
		unsigned char *buffer = new unsigned char[file_size];

		PHYSFS_read(file, buffer, 1, file_size);
		PHYSFS_close(file);

		// create the font
		FTFont *ftfont = new FTGLTextureFont ( buffer, file_size, hinting );
		ftfont->FaceSize(size);

		if (ftfont->Error() == 0)
		{
			font = new Font( ftfont, buffer, _Device );
			clog << ">>> Font '"<< filename << "' ("<< size << ") loaded"
					<< endline;
		}
		else
		{
			delete ftfont;
			clog << "!!! Font '"<< filename << "' could not be loaded"
					<< endline;
			font = new Font();
		}
	}
	else
	{
		clog << "!!! Font '"<< filename << "' not found"<< endline;
		font = new Font();
	}

	_fonts[key] = font;

	return font;
}

//------------------------------------------------------------------------------
Font *FontManager::getDefaultFont()
{
	return _DefaultFont.pointer();
}

//------------------------------------------------------------------------------
Font::Font()
{
	_Font = 0;
	_Buffer = 0;
	clog << ">>> Font constructed..."<< endline;
}

//------------------------------------------------------------------------------
Font::Font(FTFont *font, unsigned char *buffer, RenderDevice* device) :
	_Font(font), _Buffer(buffer), _Device(device)
{
	clog << ">>> Font constructed..."<< endline;
}

//------------------------------------------------------------------------------
Font::~Font()
{
	destroy();
	clog << ">>> Font destructed..."<< endline;
}

//------------------------------------------------------------------------------
void Font::destroy()
{
	delete _Font;
	delete [] _Buffer;
	clog << ">>> Font destroyed..."<< endline;
}

//------------------------------------------------------------------------------
void Font::print(float x, float y, const std::string &text, int halign,
		int valign) const
{
	if (_Font == 0)
		return;

	if (_Device.valid() == false)
		return;

	_Device->begin2D();

	if (x < 0.0)
		x += _Device->getViewportWidth();
	else if (x <= 1.0)
		x *= _Device->getViewportWidth();

	if (halign == 0)
		x -= _Font->Advance(text.c_str() ) / 2;
	else if (halign == -1)
		x -= _Font->Advance(text.c_str() );

	if (y < 0.0)
		y += _Device->getViewportHeight();
	else if (y <= 1.0)
		y *= _Device->getViewportHeight();

	y -= _Font->Descender();
	if (valign == 0)
		y -= _Font->LineHeight() / 2;
	else if (valign == -1)
		y -= _Font->LineHeight();

	glEnable(GL_TEXTURE_2D);
	glPushMatrix();
	glLoadIdentity();
	glTranslatef(x, y, 0);
	_Font->Render(text.c_str() );
	glPopMatrix();

	_Device->end2D();
}

} // namespace BlueCore
