/**********************************************************************
 
	Copyright (C) 2003-2004
	Hirohisa MORI <joshua@nichibun.ac.jp>
	Tomoki SEKIYAMA <sekiyama@yahoo.co.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/


#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include "long_char.h"
#include "memory_debug.h"
#include "ft2engine.h"
#include "stream.h"
#include "lc_encode.h"

extern LC_FONT_ENGINE ft2_font_engine;

int ft_error_code;

/*
#define FT_ASSERT(a)	\
	ft_error_code = a; \
	do if ( ft_error_code ) { \
		printf("FreeType2 Error: %s  [Code = %d]\n", #a, ft_error_code); \
		er_panic("freetype"); \
	} while ( 0 )
*/

#define FT_ASSERT(a)	\
	if ( (a) )		\
		ss_printf("FT_ASSERT ERROR (%s %i)\n",__FILE__,__LINE__);


#define TH 0

struct METRICS {
	int w, h;
	int bx, by;
	int adv;
	
	int index;
	FT_Face face;
	int charmaps;
	FT_Glyph glyph;
};

int
get_face_and_index(
	struct METRICS * mtr,
	FT2_FONT_WORK * ft2_fw,
	LCF_SET * set)
{
FT2_FONTREC * fr;
int i,j,k;
FT2_ENCODE_INFO * ei;
FT_Face face;
int index;
int err;
int size;
L_CHAR ch;

	ch = set->ch;
	size = set->size;
	for ( fr = ft2_fw->fr ; fr;
		fr = (ft2_fw->type == FWT_ALL_LIST ?
			fr->all_next :
			fr->family_next) ) {
		for ( i = 0 ; i < fr->size_len ; i ++ ) {
			if ( ft2_fw->scalable == 0 ) {
				if ( fr->sizes[i] != size )
					continue;
			}
			else {
				if( fr->sizes[i] != -1 )
					continue;
			}
			for ( j = 0 ; j < fr->work_face->num_charmaps ; 
						j ++ ) {
				ei = &fr->encode_info[j];
				for ( k = 0 ; k < 2 ; k ++ ) {
					if ( ei->lc_lcz[k] == LCC_ERROR )
						continue;
					if ( (ei->lc_lcz[k]&ei->lc_mask[k])
							!=
						(ch&ei->lc_mask[k]) )
						continue;
					break;
				}
				if ( k == 2 )
					continue;
				face = fr->work_face;
				err = FT_Select_Charmap(face,
					face->charmaps[j]->encoding);
				if ( err )
					er_panic(">>>");
				index = FT_Get_Char_Index(face, 
						ch & (~ei->lc_mask[k]) );
				if ( index == 0 )
					continue;
				goto next;
			}
		}
	}
	mtr->face = 0;
	mtr->index = 0;
	mtr->charmaps = 0;
	return -1;
next:

/*
ss_printf("FACE = %x %i %i %s\n",face,size,ft2_fw->fr->sizes[0],
		ft2_fw->fr->path);
*/
	FT_ASSERT( FT_Set_Char_Size(face, 0, size*64/10, 72, 72) );

	mtr->face = face;
	mtr->index = index;
	mtr->charmaps = j;
	return 0;
}

void
ft2_get_string_pic(
	LC_STRING_PIC * p,
	int dir,
	LCF_SET * str,
	int	len)
{
FT_BitmapGlyph glyph_bitmap;
LC_FONT * lcf;
LC_FONT_WORK * fw;
FT2_FONT_WORK * ft2_fw;
struct METRICS *metrics;
int w, h, ws, hs;
int x0, x_st, y_st, hang;
int i, x, y, y1, y2;
unsigned char *src, *dst;
int size;
int err;

	lcf = str->font;
	size = str->size;
	fw = get_font_work(lcf,&ft2_font_engine);
	p->pic = 0;
	if ( fw == 0 ) {
		p->pic = 0;
		return;
	}
	ft2_fw = (FT2_FONT_WORK*)fw->work;

	metrics = (struct METRICS*)d_alloc(sizeof(struct METRICS) * len);
	w = h = 0;
	y1 = y2 = 0;
	for ( i = 0 ; i < len ; i++ ) {
//		c = str[i].ch & (~get_lc_mask(str[i].ch));
		if ( get_face_and_index(&metrics[i],ft2_fw,&str[i]) < 0 )
			continue;
		if ( metrics[i].index ) {
			FT_ASSERT( FT_Load_Glyph(metrics[i].face, 
					metrics[i].index, FT_LOAD_DEFAULT) );
			FT_ASSERT( FT_Get_Glyph(metrics[i].face->glyph, 
					&metrics[i].glyph) );
			metrics[i].w = (metrics[i].face
				->glyph->metrics.width+TH) >> 6;
			metrics[i].h = (metrics[i].face
				->glyph->metrics.height+TH) >> 6;
			metrics[i].bx = (metrics[i].face
				->glyph->metrics.horiBearingX+TH) >> 6;
			metrics[i].by = (metrics[i].face
				->glyph->metrics.horiBearingY+TH) >> 6;
			metrics[i].adv = (metrics[i].face
				->glyph->metrics.horiAdvance+TH) >> 6;
			w += metrics[i].adv;
			if ( y1 > -metrics[i].by )
				y1 = -metrics[i].by;
			if ( y2 < metrics[i].h - metrics[i].by )
				y2 = metrics[i].h - metrics[i].by;
			if ( i > 0 && FT_HAS_KERNING(metrics[i].face) ) {
				FT_Vector delta;
				FT_Get_Kerning(metrics[i].face, 
					metrics[i-1].index, 
					metrics[i].index, 0, &delta);
				metrics[i].adv -= delta.x >> 6;
				w -= delta.x >> 6;
			}
		}
	}
	
	x0 = 0;
	if ( metrics[0].bx < 0 ) {
		w -= metrics[0].bx;
		x0 = - metrics[0].bx;
	}
	hang = metrics[len-1].bx + metrics[len-1].w;
	if ( hang > metrics[len-1].adv )
		w += hang - metrics[len-1].adv;
	h = y2 - y1;
	if ( h == 0 ) {
		y1--;
		h++;
	}
	
	p->r.tl.x = 0;
	p->r.br.x = w;
	p->r.tl.y = y1;
	p->r.br.y = y2;
	p->width = w;

	if ( w )
		p->pic = d_alloc(w*h);
	else
		p->pic = 0;
	for ( i = 0 ; i < w*h ; i++ )
		p->pic[i] = 255;
	
	for ( i = 0 ; i < len ; i++ ) {
		if ( metrics[i].index ) {
			err = FT_Select_Charmap(metrics[i].face,
					metrics[i].face
					->charmaps[metrics[i].charmaps]
					->encoding);
			if ( err )
				er_panic(">>>");

			if ( metrics[i].glyph->format != ft_glyph_format_bitmap )
				FT_ASSERT( FT_Glyph_To_Bitmap(&metrics[i].glyph, ft_render_mode_normal, 0, 1) );
			glyph_bitmap = (FT_BitmapGlyph)metrics[i].glyph;
			src = glyph_bitmap->bitmap.buffer;
			ws = glyph_bitmap->bitmap.width;
			hs = glyph_bitmap->bitmap.rows;
			x_st = metrics[i].bx;
			y_st = -y1-metrics[i].by;
			
			for ( y = 0 ; y < hs ; y++ ) {
				dst = &p->pic[x0+x_st + (y_st+y)*w];
				for ( x = 0 ; x < ws ; x++ ) {
					*dst = (((int)*dst+1)*(256-(int)*src)>>8)-1;
					src++;
					dst++;
				}
			}
			x0 += metrics[i].adv;
			
			FT_Done_Glyph(metrics[i].glyph);
		}
	}
	
	d_f_ree(metrics);
	return;
}

