#include ".\gamettf.h"

#ifdef ENABLE_TTF_EXTENSION

GameTTF::GameTTF(FT_Library *library)
{
	// salviamo pointer libreria
	this->library=library;
	_faceLoaded=false;
	mbuffer=NULL;
	// ancora nulla in cache
	FAMCACHER=NULL;
	CACHER=NULL;	// puntatore all'ultimo font settato in setsize
	CURRFAMCACHE=NULL;// puntatore all'ultima famiglia di font settata in setsize
}

GameTTF::~GameTTF(void)
{
	// ripulisci totalmente la cache dei glyph
	FlushCache(-1,-1);

	// distruggi il face se caricato
	if (_faceLoaded) {
		FT_Done_Face    ( face );
		_faceLoaded=false;
	}

	// libera buffer
	free(mbuffer);
}

// pulisci la nostra cache
void GameTTF::FlushCache(int cachedwidth, int cachedheight)
{
	int c;

	if (!FAMCACHER)
		return;	// niente da flushare

	// decidi quale famiglia cachata bisogna eliminare
	struct FamCacher *fcache = FAMCACHER;
	while (fcache) {
		if (
			(cachedwidth == -1 && cachedheight == -1) ||
			(fcache->width == cachedwidth && fcache->height == cachedheight)
			)
			fcache->toBeDeleted=true;
		fcache=fcache->next;
	}

	// elimina le famiglie che vanno eliminate
	struct FamCacher *ftmp, *fprev=NULL;
	fcache = FAMCACHER;

	while (fcache) {
		// va cancellato
		if (fcache->toBeDeleted) {
			ftmp = fcache;
			if (!fprev) {
				// head remove
				// (a)->b->c...
				fcache=fcache->next;
				FAMCACHER = fcache;
			} else {
				// middle remove
				// a->(b)->c...
				fcache = fcache->next;
				fprev->next = fcache;
			}
			// elimina struct glyph nella famiglia corrente e dati glyps, se caricati
			for (c=0; c < MAX_CACHE_CHAR; c++) {
				if (ftmp->CACHER[c].glyph_loaded)
					FT_Done_Glyph( ftmp->CACHER[c].glyph );
			}
			delete ftmp;
		} // va cancellato
		else {
			// non va cancellato, prova il prox
			fprev = fcache;
			fcache=fcache->next;
		}
	}

} // FlushCache

// carica il font
bool GameTTF::LoadFont(const TCHAR *datfile, const TCHAR *fontfile)
{
	int error;

	// load and decompress font file
	LZArchive lza;
	LZItem lzi;

	if (lza.open(datfile) == false)
		return false;
	if (lza.read(fontfile, &lzi) == false)
		return false;

	lzi.decompress();
    unsigned char *buffer = (unsigned char *)lzi.dBuffer();
	unsigned long buffersize = lzi.dSize();

	// fai una copia interna del buffer
	free(mbuffer);
	mbuffer=NULL;

	mbuffer = (unsigned char *)malloc(sizeof(unsigned char)*buffersize);
	for (unsigned int b=0; b < buffersize; b++)
		mbuffer[b]=buffer[b];


	// distruggi face se già caricato
	if (_faceLoaded) {
		PFrogConsole::instance()->addconsole(0, "DELETING PREVIOUS ALLOCATED FONT FACE");
		FT_Done_Face    ( face );
		_faceLoaded=false;
	}

	// carica il face
	error = FT_New_Memory_Face( 
		*library, 
		mbuffer, /* first byte in memory */  
		buffersize, /* size in bytes */  
		0, /* face_index */  
		&face ); 
	
	if ( error ) { 
		PFrogConsole::instance()->addconsole(0, "CANNOT LOAD FONT FACE");
		return false;
	}

	// seleziona un charmap, se non già presente
	if (!face->charmap) { 
		for(int u=0;u< face->num_charmaps;u++) {
			error = FT_Set_Charmap( face, /* target face object */  
				face->charmaps[u]); /* encoding */
			break;
	#if 0
			if(face->charmaps[u]->encoding == FT_ENCODING_ADOBE_CUSTOM) {
				//FT_Set_Charmap(face,face->charmaps[u]);
				break;
			}
	#endif
		}
	}

	if ( error || !face->charmap ) { 
		PFrogConsole::instance()->addconsole(0, "CANNOT SET ENCODING");
		FT_Done_Face    ( face );
		return false;
	}

	PFrogConsole::instance()->addconsole(0, "FONT FACE LOADED!");
	_faceLoaded=true;

	// Setta un size di default
	this->SetSize(16,0,Color(255,255,255), Color(0,0,0));

	return true;
} // LoadFont

bool GameTTF::SetSize(int width, int height, Pixel foreColor, Pixel backColor)
{
	int c, error;

	if (!_faceLoaded) {
		PFrogConsole::instance()->addconsole(0, "CANNOT SET FONT SIZE, FACE OR LIBRARY NOT LOADED!");
		return false;
	}

	error = FT_Set_Pixel_Sizes( 
		face, /* handle to face object */  
		width, /* pixel_width */  
		height ); /* pixel_height */

	if (error) {
		PFrogConsole::instance()->addconsole(0, "CANNOT SET FONT SIZE, API ERROR!");
		return false;
	}

	//PFrogConsole::instance()->addconsole(0, "SET FONT SIZE %d, %d", width, height);

	// se quella famiglia non è ancora cachata, crea struct di caching ora!
	bool cacheFound=false;
	struct FamCacher *tmpc=FAMCACHER;
	while (tmpc) {
		if (tmpc->width == width && tmpc->height == height) {
			cacheFound=true;
			break;
		}
		tmpc=tmpc->next;
	}

	// crea struct caching famiglia
	if (!cacheFound) {
		tmpc = new FamCacher;
		tmpc->next = FAMCACHER;
		FAMCACHER=tmpc;

		tmpc->last_update = PocketPC::GetTickCount();
		tmpc->toBeDeleted=false;
		tmpc->width = width;
		tmpc->height = height;
		
		// nessun glyph caricato ancora
		for (c=0; c < MAX_CACHE_CHAR; c++)
			tmpc->CACHER[c].glyph_loaded=false;
	
		PFrogConsole::instance()->addconsole(0, "CREATED FAMILY FONT CACHE: %d, %d", width, height);
	}

	// ora salva puntatore dell'ultimo font cachato / Creato qui
	CURRFAMCACHE = tmpc;
	CACHER = CURRFAMCACHE->CACHER;

	// salva colore
	forecolor=foreColor;

	return true;
}// SetSize

// disegna un carattere, cachandolo se necessario
// se display è NULL, non lo disegna ma provvede soltanto al caching
//
bool GameTTF::DrawChar(Display *display, int x, int y, char c)
{
	int error;
	unsigned char uc=(unsigned)c;

	if (!_faceLoaded) {
		PFrogConsole::instance()->addconsole(0, "CANNOT DRAW CHAR, FACE OR LIBRARY NOT LOADED!");
		return false;
	}

	// il carattere è nei limiti della nostra cache
	if (uc >= MAX_CACHE_CHAR) {
		PFrogConsole::instance()->addconsole(0, "DRAWCHAR CODE NOT CACHABLE %d", uc);
		return false;
	}

	// se il carattere non è cachato, dobbiamo procedere con il caching
	if ( !CACHER[uc].glyph_loaded ) {

		// Get glyph index and load it
		unsigned int glyph_index = FT_Get_Char_Index( face, c );
		error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );

		if (error) {
			PFrogConsole::instance()->addconsole(0, "CANNOT LOAD GLYPH!");
			return false;
		}

		/* extract glyph image and store it in our table */  
		error = FT_Get_Glyph( face->glyph, &CACHER[uc].glyph );
		if (error) {
			PFrogConsole::instance()->addconsole(0, "CANNOT GET GLYPH IMAGE!");
			return false;
		}

		error = FT_Glyph_To_Bitmap( &CACHER[uc].glyph, FT_RENDER_MODE_NORMAL, 
			NULL, 1 );
		if (error) {
			FT_Done_Glyph( CACHER[uc].glyph );
			PFrogConsole::instance()->addconsole(0, "CANNOT RENDER GLYPH IMAGE!");
			return false;
		}

		CACHER[uc].bit = (FT_BitmapGlyph)CACHER[uc].glyph;

		// cacha info utili di questo carattere
		// todo: font_size_family?
		CACHER[uc].advance = FT_CEIL(face->glyph->metrics.horiAdvance);
		CACHER[uc].maxy = FT_FLOOR(face->glyph->metrics.horiBearingY);
#if 0
		CACHER[uc].minx = FT_FLOOR(face->glyph->metrics.horiBearingX);
		if ( FT_IS_SCALABLE( face ) ) {
			CACHER[uc].maxx = CACHER[uc].minx + FT_CEIL(face->glyph->metrics.width);
			//CACHER[uc].miny = CACHER[uc].maxy - FT_CEIL(face->glyph->metrics.height);
		} else {
			CACHER[uc].maxx = CACHER[uc].minx + FT_CEIL(face->glyph->metrics.horiAdvance);
			//CACHER[uc].miny = CACHER[uc].maxy - FT_CEIL(face->available_sizes[font->font_size_family].height);
		}
#endif
		CACHER[uc].glyph_loaded=true;
		
		PFrogConsole::instance()->addconsole(0, "CACHED CHAR %d", uc);
	} // controlla caching

	int fx, fy;
	int decrement = y - CACHER[uc].maxy;

	// infine disegna il carattere
	if (display)
		display->SetPixelShader(&charshader);

	for ( int i = 0; i < CACHER[uc].bit->bitmap.width; i++ ) {
		for ( int j = 0; j < CACHER[uc].bit->bitmap.rows; j++ ) {

			fx = i + x;
			fy = j + decrement;

			charshader.pixMapBlending = CACHER[uc].bit->bitmap.buffer[j * 
				CACHER[uc].bit->bitmap.width + i];

			if (display)
				display->SetPixel(fx, fy, forecolor );
		}
	}		

	if (display)
		display->SetDefaultShader();

	return true;
} // DrawChar


unsigned long GameTTF::DrawTextEx(Display *display, int x, int y, char *pattern, ...)
{

	// set new text
	va_list vl;
	va_start(vl, pattern);
	vsprintf(textbuffer, pattern, vl);
	va_end(vl);

	return DrawText(display, x, y, textbuffer);

} // DrawText

unsigned long GameTTF::DrawText(Display *display, int x, int y, char *textbuffer)
{
	unsigned char uc;
	int current_x = x;
	unsigned long width=0;

	for (unsigned int t=0; t < strlen(textbuffer); t++) { 
		uc = (unsigned)textbuffer[t];

		if (!this->DrawChar(display, current_x, y, uc))
			continue;
		current_x += CACHER[uc].advance;
		width += CACHER[uc].advance;
	}

	return width;
} // // DrawText


#endif
