#include "ttf_renderer.h"
#include "video.h"

#include <iostream>


#ifdef Revision
#undef Revision
#endif
#define Revision "ttf_renderer.cpp $Revision: 1.9 $"

void UTF8_to_UNICODE ( std::vector<int> *unicode, const char *utf8, int len );
static inline int UNICODE_strlen ( const Uint16 *text );


FT_Library  library;
Text_Renderer* _get_textrenderer ( void )
{
	return new TTF_Renderer;
}

bool TTF_Renderer::_init()
{
	FT_Error error;
	error = FT_Init_FreeType ( &library );
	if ( error )
	{
		_last_error_="Error while initializing freetype library";
		return false;
	}
	return true;
}

bool TTF_Renderer::init_font ( std::string alias, std::string name )
{
	FT_Face *face;
	face=new FT_Face;
	FT_Error error;
	error = FT_New_Face ( library, name.c_str(), 0, face );
	if ( error == FT_Err_Unknown_File_Format )
	{
		_last_error_="Unknow file format: \""+name+"\"";
		delete face;
		return false;
	}
	else if ( error )
	{
		_last_error_="Cant open file, or file is broken: \""+name+"\"";
		delete face;
		return false;
	}
	FT_Select_Charmap ( *face, FT_ENCODING_UNICODE );
	_fonts_.insert ( alias, face );
	std::cerr<<"Font "<<alias<<" intialized from file "<<name<<std::endl;
	return true;
}


bool TTF_Renderer::render_text_line ( std::string video_name, std::string alias, std::string text, VideoColor color, int size, std::string surface_name, std::string group_name, bool overwrite )
{
	//Сконвертим строку в уникод из utf-8
        std::vector<int> unicode;
	UTF8_to_UNICODE ( &unicode, text.c_str(), text.length() );
	int origin = 0;
        return render_text_line( video_name, alias, unicode, color, size, surface_name, group_name, overwrite, &origin );
}
bool TTF_Renderer::render_text_line ( std::string video_name, std::string alias, const std::vector<int> &text, VideoColor color, int size, std::string surface_name, std::string group_name, bool overwrite, int *origin_out )
{
	FT_Error error;
	FT_Face *face;
	if ( !_fonts_.exist ( alias ) )
	{
		_last_error_="No such alias:\""+alias+"\"";
		return false;
	}
	face=_fonts_.get ( alias );

	error = FT_Set_Pixel_Sizes ( *face, 0, size );
	if ( error )
	{
		_last_error_="Error setting face size";
		return false;
	}
	uint16_t *unicode;
	int len=text.size();
	unicode=new uint16_t[len+1];
	for ( int a=0;a<len;a++ ) unicode[a]=text[a];
	unicode[len]=0;

	int w_total,h_total;
	std::vector<line_info> linfo;
	_get_size_ ( face, unicode, &linfo, &w_total, &h_total, len );
	//Создадим поверхность
	Video_Interface *video=video_get_interface ( video_name );
	Video_Surface *surface;
	if ( !video->_create_surface ( w_total, h_total, surface_name, group_name, overwrite ) )
	{
		_last_error_="Cant create surface: w:"+w_total;
		_last_error_+="h:"+h_total;
		_last_error_+=":"+video->_get_error();
		delete[] unicode;
		return false;
	}
	surface = video->_get_surface ( surface_name, group_name );
	int ls=surface->_get_line_size();
	int bpp=surface->_get_bpp();
	uint8_t *data1=static_cast<uint8_t*> ( surface->_get_surface_data() );
	memset ( data1, 0, ls*h_total );

	int pen_x, pen_y, n;
	pen_x = 0;
	pen_y = 0;
	int a=0;
	int y_off=h_total-linfo[0].h;
	y_off=y_off>0?y_off-1:y_off;
	if ( origin_out ) *origin_out=linfo[0].origin;
	for ( n = 0; n < len; n++ )
	{
		int w,h, origin;
		w=linfo[a].w;
		h=linfo[a].h;
		origin=linfo[a].origin;
		if ( n+1<len )
		{
			if ( unicode[n]=='\\' && unicode[n+1]=='n' )
			{
				y_off-=h;
				if ( y_off<0 ) y_off=0;
				pen_x=0;
				n++;
				a++;
				continue;
			}
		}
		/* load glyph image into the slot (erase previous one) */
		error = FT_Load_Char ( *face, unicode[n], FT_LOAD_RENDER );
		FT_GlyphSlot  slot = ( *face )->glyph;  /* a small shortcut */
		if ( error ) continue;  /* ignore errors */
		for ( int y=0, y1=origin- ( slot->bitmap.rows-slot->bitmap_top );y<=slot->bitmap.rows-1;y++, y1++ )
		{
			if ( y1+y_off>=h_total ) continue;
			for ( int x=0;x<slot->bitmap.width;x++ )
			{
				uint8_t clr;
				clr=* ( slot->bitmap.buffer+ ( ( slot->bitmap.rows-1 )-y ) *slot->bitmap.width+x );
				clr= ( clr>color.ai ) ?color.ai:clr;
				* ( data1+ ( y_off+y1 ) *ls+ ( pen_x+x ) *bpp ) = color.ri;
				* ( data1+ ( y_off+y1 ) *ls+ ( pen_x+x ) *bpp+1 ) = color.gi;
				* ( data1+ ( y_off+y1 ) *ls+ ( pen_x+x ) *bpp+2 ) = color.bi;
				* ( data1+ ( y_off+y1 ) *ls+ ( pen_x+x ) *bpp+3 ) = clr;
			}
		}
		/* increment pen position */
		pen_x += slot->advance.x >> 6;
	}
	surface->_update_();
//	mprotect(surface->_get_surface_data(), surface->_get_line_size()*surface->_get_height(), PROT_READ);
	delete[] unicode;
	return true;
}

bool TTF_Renderer::render_text_line_to_sprite ( std::string video_name, std::string alias, const std::vector<int> &text, VideoColor color, int size, std::string surface_name, std::string group_name, bool overwrite )
{
	int origin;
	if ( !render_text_line ( video_name, alias, text, color, size, surface_name, group_name, overwrite, &origin ) ) return false;
	Video_Interface *vid=video_get_interface ( video_name );
	if(!vid)
	{
		_last_error_="Can't get video driver: "+video_error();
		return false;
	}
	if ( !vid->_create_sprite ( surface_name, group_name, surface_name, group_name, true ) )
	{
		_last_error_="Can't create sprite: "+vid->_get_error();
		return false;
	}

	Video_Sprite *spr=vid->_get_sprite ( surface_name, group_name );
	if ( !spr )
	{
		_last_error_= "Can't get sprite: "+vid->_get_error() ;
		return false;
	}

	spr->_set_base_point ( 0, origin );
	return true;
}

void TTF_Renderer::_get_size_ ( FT_Face *face, uint16_t *text, std::vector<line_info> *linfo, int *w_total, int *h_total, int len )
{
	*w_total=*h_total=0;
	int w=0, h=0;
	FT_GlyphSlot slot = ( *face )->glyph;
	FT_Error error;
	int bh=0;
	for ( int a=0;a<len;a++ )
	{
		if ( a+1<len )
		{
			if ( text[a]=='\\' && text[a+1]=='n' )
			{
				linfo->push_back ( line_info ( w/64, h/64 + bh/64, bh/64 ) );
                                *w_total= ( *w_total>w/64 ) ?*w_total:w/64;
				*h_total += h/64 + bh/64;
                                w=h=bh=0;
				a++;
                                continue;
			}
		}
		/* load glyph image into the slot (erase previous one) */
		error = FT_Load_Char ( *face, text[a], FT_LOAD_RENDER );
		if ( error ) continue;  /* ignore errors */
		h= ( slot->metrics.height>h ) ?slot->metrics.height:h;
		bh= ( slot->metrics.height-slot->metrics.horiBearingY > bh ) ?slot->metrics.height-slot->metrics.horiBearingY : bh;
		w+=slot->metrics.horiAdvance;
	}
	linfo->push_back ( line_info ( w/64, h/64 + bh/64, bh/64 ) );
	*w_total= ( *w_total>w/64 ) ?*w_total + 1:w/64 + 1; 
	*h_total+=h/64 + bh/64 ;
}

void UTF8_to_UNICODE ( std::vector<int> *unicode, const char *utf8, int len )
{
	int ch;

	for ( int i=0; i < len; ++i )
	{
		ch = ( ( const unsigned char * ) utf8 ) [i];
		if ( ch >= 0xF0 )
		{
			ch  = ( Uint16 ) ( utf8[i]&0x07 ) << 18;
			ch |= ( Uint16 ) ( utf8[++i]&0x3F ) << 12;
			ch |= ( Uint16 ) ( utf8[++i]&0x3F ) << 6;
			ch |= ( Uint16 ) ( utf8[++i]&0x3F );
		}
		else
			if ( ch >= 0xE0 )
			{
				ch  = ( Uint16 ) ( utf8[i]&0x0F ) << 12;
				ch |= ( Uint16 ) ( utf8[++i]&0x3F ) << 6;
				ch |= ( Uint16 ) ( utf8[++i]&0x3F );
			}
			else
				if ( ch >= 0xC0 )
				{
					ch  = ( Uint16 ) ( utf8[i]&0x1F ) << 6;
					ch |= ( Uint16 ) ( utf8[++i]&0x3F );
				}
		unicode->push_back( ch );
	}
}
static __inline__ int UNICODE_strlen ( const Uint16 *text )
{
	int size = 0;
	while ( *text++ )
	{
		++size;
	}
	return size;
}


