/**********************************************************************
 
	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	<stdio.h>
#include	<stdlib.h>
#include	<sys/param.h>

#include <ft2build.h>
#include FT_FREETYPE_H

#include	"memory_debug.h"
#include	"ft2engine.h"
#include	"encoding_tables.h"
#include	"utils.h"
#include	"xl.h"
#include	"task.h"
#include	"pri_level.h"
#include	"lock_level.h"
#include	"lc_encode.h"
#include	"avt.h"
#include	"xlerror.h"

int
__ft2_do(int(*func)(void*), void* arg,char * _file,int _line);
#define ft2_do(f,arg) __ft2_do((f),(arg),__FILE__,__LINE__)


int ft2_get_font();
void ft2_get_string_pic();
int ft2_font_coverage();
XL_SEXP *
ft2_FontRec(XLISP_ENV * e,XL_SEXP * s,XLISP_ENV * a,XL_SYM_FIELD * sf);
XL_SEXP *
ft2_LoadFontIndex(XLISP_ENV * e,XL_SEXP * s,XLISP_ENV * a,XL_SYM_FIELD * sf);

LC_FONT_ENGINE_TYPE ft2_font_engine_type = {
	"freetype2",
	ft2_get_font,
	ft2_get_string_pic,
	ft2_font_coverage,
	{0.0},
	0
};

LC_FONT_ENGINE ft2_font_engine = {
	0,
	"freetype2",
	&ft2_font_engine_type,
	0,
};

FT_Library ft2_library;

AVT_NODE * ft2_fontrec_all_tree;
AVT_NODE * ft2_fontrec_family_tree;

void ft2_task();
SEM ft2_lock;
struct ft2_task_queue_t {
	int	(*func)(void*);
	void *	arg;
	int	ret;
	char *	file;
	int	line;
	struct ft2_task_queue_t * next;
} * ft2_task_queue, * ft2_task_queue_last;


void
ft2engine_init()
{



	if ( FT_Init_FreeType(&ft2_library) )
		er_panic("ft2engine_init");

	ft2_lock = new_lock(LL_FT2_ENGINE);
	ft2_task_queue = 0;
	ft2_task_queue_last = 0;
	create_task(ft2_task,0,PRI_V_QUEUE);

	insert_font_engine(&ft2_font_engine);

	set_env(gblisp_top_env1,l_string(std_cm,"FontRec"),
		get_func_prim(ft2_FontRec,FO_NORMAL,0,5,-1));
	set_env(gblisp_top_env1,l_string(std_cm,"LoadFontIndex"),
		get_func_prim(ft2_LoadFontIndex,FO_APPLICATIVE,0,2,2));

}


int
cmp_fontrec(FT2_FONTREC * fr1,FT2_FONTREC * fr2)
{
int ret;
	ret = l_strcmp(fr1->family,fr2->family);
	if ( ret != 0 )
		return ret;
	return l_strcmp(fr1->style,fr2->style);
}


int
cmp_fontrec_family(FT2_FONTREC * fr1,FT2_FONTREC * fr2)
{
	return l_strcmp(fr1->family,fr2->family);
}


void
insert_fontrec_tree(
	L_CHAR * path,
	int face,
	L_CHAR * family,
	L_CHAR * style,
	int * sizes)
{
AVT_NODE * n, * n2;
FT2_FONTREC * fr;
int len;
int f;

	n = d_alloc(sizeof(*n));
	fr = d_alloc(sizeof(*fr));
	memset(fr,0,sizeof(*fr));
	fr->path = ln_copy_str(&utf8_cm,path);
	fr->face = face;
	fr->family = ll_copy_str(family);
	fr->style = ll_copy_str(style);
	fr->all_next = 0;
	fr->family_next = 0;
	fr->active = 0;
	if ( sizes == 0 ) {
		fr->sizes = 0;
		fr->size_len = 0;
	}
	else {
		for( len = 0 ; sizes[len] ; len ++ );
		fr->sizes = d_alloc(sizeof(int)*(len+1));
		memcpy(fr->sizes,sizes,sizeof(int)*(len+1));
		fr->size_len = len;
	}
	n->data = fr;
	f = 1;
	n2 = avt_insert(&ft2_fontrec_all_tree,n,cmp_fontrec);
	if ( n2 != n ) {
		fr->all_next = n2->data;
		n2->data = fr;
		d_f_ree(n);
	}

	n = d_alloc(sizeof(*n));
	n->data = fr;
	n2 = avt_insert(&ft2_fontrec_family_tree,n,cmp_fontrec_family);
	if ( n2 != n ) {
		fr->family_next = n2->data;
		n2->data = fr;
		d_f_ree(n);
	}
}

char *
read_fil_str(int inp)
{
char * ret;
short len;

	if ( u_read(inp,&len,sizeof(len)) < sizeof(len) )
		return 0;
	ret = d_alloc(len);
	if ( u_read(inp,ret,len) < len ) {
		d_f_ree(ret);
		return 0;
	}
	return ret;
}

int *
read_fil_int(int inp)
{
int * ret;
short len;

	if ( u_read(inp,&len,sizeof(len)) < sizeof(len) )
		return 0;
	ret = d_alloc(len);
	if ( u_read(inp,ret,len) < len ) {
		d_f_ree(ret);
		return 0;
	}
	return ret;
}

int
ft2_bin_font_index_loader(char * path)
{
int inp;
char * _path;
int * _face;
char * _family;
char * _style;
int * _size;

	inp = u_open(path,O_RDONLY);
	if ( inp < 0 )
		return -1;
	for ( ; ; ) {
		_path = _family = _style = 0;
		_face = _size = 0;
		_path = read_fil_str(inp);
		if ( _path == 0 )
			break;
		_face = read_fil_int(inp);
		if ( _face == 0 )
			break;
		_family = read_fil_str(inp);
		if ( _family == 0 )
			break;
		_style = read_fil_str(inp);
		if ( _style == 0 )
			break;
		_size = read_fil_int(inp);
		if ( _size == 0 )
			break;

		insert_fontrec_tree(
			l_string(&utf8_cm,_path),
			_face[0],
			l_string(&utf8_cm,_family),
			l_string(&utf8_cm,_style),
			_size);

		d_f_ree(_path);
		d_f_ree(_face);
		d_f_ree(_family);
		d_f_ree(_style);
		d_f_ree(_size);
	}
	if ( _path )
		d_f_ree(_path);
	if ( _face )
		d_f_ree(_face);
	if ( _family )
		d_f_ree(_family);
	if ( _style )
		d_f_ree(_style);
	if ( _size )
		d_f_ree(_size);
	u_close(inp);
	return 0;
}
XL_SEXP *
ft2_LoadFontIndex(XLISP_ENV * e,XL_SEXP * s,XLISP_ENV * a,XL_SYM_FIELD * sf)
{
L_CHAR * target;
char * _target;
XL_SEXP * lpath;


	lpath = get_el(s,1);
	if ( get_type(lpath) != XLT_STRING )
		goto type_missmatch;
	target = get_script(0,lpath->string.data);
	_target = ln_copy_str(std_cm,target);
	ft2_bin_font_index_loader(_target);
	d_f_ree(target);
	d_f_ree(_target);
	return 0;
type_missmatch:
	return get_error(
		s->h.file,
		s->h.line,
		XLE_SEMANTICS_TYPE_MISSMATCH,
		l_string(std_cm,"LoadFontIndex"),
		list(	n_get_string("type_missmatch"),
			0));
}


XL_SEXP *
ft2_FontRec(XLISP_ENV * e,XL_SEXP * s,XLISP_ENV * a,XL_SYM_FIELD * sf)
{
XL_SEXP * ss, * d;
L_CHAR * _path;
int _face;
L_CHAR * _family;
L_CHAR * _style;
int * _size;
int len,i;
L_CHAR * er;

	d = 0;
	ss = get_el_by_symbol(s,er=l_string(std_cm,"path"),0);
	if ( ss == 0 )
		goto err;
	d = eval(e,get_el(ss,1));
	if ( get_type(d) != XLT_STRING )
		goto err;
	_path = d->string.data;

	ss = get_el_by_symbol(s,er=l_string(std_cm,"face"),0);
	if ( ss == 0 )
		goto err;
	d = eval(e,get_el(ss,1));
	if ( get_type(d) != XLT_INTEGER )
		goto err;
	_face = d->integer.data;

	ss = get_el_by_symbol(s,er=l_string(std_cm,"family"),0);
	if ( ss == 0 )
		goto err;
	d = eval(e,get_el(ss,1));
	if ( get_type(d) != XLT_STRING )
		goto err;
	_family= d->string.data;

	ss = get_el_by_symbol(s,er=l_string(std_cm,"style"),0);
	if ( ss == 0 )
		goto err;
	d = eval(e,get_el(ss,1));
	if ( get_type(d) != XLT_STRING )
		goto err;
	_style = d->string.data;

	ss = get_el_by_symbol(s,er=l_string(std_cm,"size"),0);
	if ( ss == 0 )
		goto err;
	len = list_length(ss);
	_size = d_alloc(sizeof(int)*(len+1));
	i = 0;
	for ( ss = cdr(ss) ; get_type(ss) == XLT_PAIR ; ss = cdr(ss) ) {
		d = eval(e,car(ss));
		switch ( get_type(d) ) {
		case XLT_INTEGER:
			if ( d->integer.data < 0 )
				_size[i++] = -1;
			else	_size[i++] = d->integer.data*10;
			break;
		case XLT_FLOAT:
			if ( d->floating.data < 0 )
				_size[i++] = -1;
			else	_size[i++] = d->floating.data*10;
			break;
		default:
			goto err;
		}
	}
	_size[i] = 0;
	insert_fontrec_tree(
		_path,
		_face,
		_family,
		_style,
		_size);
	d_f_ree(_size);
	return 0;
err:
	if ( get_type(d) == XLT_ERROR )
		return d;


	return get_error(
		s->h.file,
		s->h.line,
		XLE_PROTO_INV_PARAM,
		l_string(std_cm,"FontRec"),
		list(	n_get_string("invalid parameter"),
			get_string(er)));
}


void
ft2_task()
{
struct ft2_task_queue_t *q;
XL_INTERPRETER * xli;

	xli = new_xl_interpreter();
	xli->a_type = XLA_SELF;
	setup_i(xli);

	lock_task(ft2_lock);
	for ( ; ; ) {
		while ( ft2_task_queue == 0 ) {
			sleep_task((int)&ft2_task_queue, ft2_lock);
			lock_task(ft2_lock);
		}

		q = ft2_task_queue;

		q->ret = (*q->func)(q->arg);

		if ( q == ft2_task_queue_last )
			ft2_task_queue_last = 0;
		ft2_task_queue = q->next;
		wakeup_task((int)q);
	}
}

int
__ft2_do(int(*func)(void*), void* arg,char * _file,int _line)
{
int ret;
struct ft2_task_queue_t *n;
	lock_task(ft2_lock);

	n = (struct ft2_task_queue_t*)d_alloc(sizeof(*n));
	n->func = func;
	n->arg = arg;
	n->next = ft2_task_queue_last;
	n->ret = 0x7fffffff;
	n->file = _file;
	n->line = _line;
	if ( ft2_task_queue == 0 )
		ft2_task_queue = n;
	ft2_task_queue_last = n;

	wakeup_task((int)&ft2_task_queue);
	for ( ; n->ret == 0x7fffffff ; ) {
		sleep_task((int)n, ft2_lock);
		lock_task(ft2_lock);
	}
	unlock_task(ft2_lock,"ft2_do");

	ret = n->ret;
	d_f_ree(n);
	return ret;
}



int
ft2_name_cmp(L_CHAR *family, L_CHAR *style, L_CHAR *fontname)
{
L_CHAR *str1, *str2;
	
	for ( str1 = family, str2 = fontname ;
			*str1 && *str2 && *str1 == *str2 ;
			str1++, str2++ );
	if ( *str1 )	/* family is not matched */
		return -1;
	if ( *str2 && *str2++ == ' ' ) {	/* style is specified */
		for ( str1 = style ;
				*str1 && *str2 && *str1 == *str2 ;
				str1++, str2++ );
		if ( *str1 || *str2 )	/* style is not matched */
			return -2;
		/* else font and style is matched */
		return 0;
	}
	else if ( *str2 )
		return -1;
	/* else style is not specified */
	return 1;
}



struct ft2_get_font_t {
	LC_FONT * font;
	LC_FONT_ENGINE * fe;
//	FT_Face * face;
	void *		font_work;
};


L_CHAR *
get_family_from_fontname(L_CHAR * fontname)
{
L_CHAR * ret;
int p;
	ret = ll_copy_str(fontname);
	for ( p = l_strlen(ret)-1 ; p >= 0 ; p -- )
		if ( ret[p] == ' ' || ret[p] == '\t' )
			break;
	if ( p < 0 )
		return ret;
	for ( ; p >= 0 ; p -- )
		if ( ret[p] != ' ' && ret[p] != '\t' )
			break;
	if ( p < 0 )
		return ret;
	ret[p+1] = 0;
	return ret;
}


L_CHAR *
get_style_from_fontname(L_CHAR *fontname)
{
int p;
	for ( p = l_strlen(fontname)-1 ; p >= 0 ; p -- )
		if ( fontname[p] == ' ' || fontname[p] == '\t' )
			break;
	if ( p < 0 )
		return 0;
	return ll_copy_str(&fontname[p+1]);
}

void
search_code_script_info(
	int code,
	CODE_SCRIPT_TABLE * tbl,
	FT2_ENCODE_INFO * ei)
{
int i;
	for ( ; tbl->script >= 0 ; tbl ++ ) {
		if ( tbl->script != code )
			continue;
		for ( i = 0 ; i < 2 ; i ++ ) {
			ei->lc_lcz[i] = tbl->lcz[i];
			ei->lc_mask[i] = tbl->mask[i];
		}
		ei->from = tbl->from;
		ei->to = tbl->to;
		break;
	}
	if ( tbl->script < 0 ) {
		for ( i = 0 ; i < 2 ; i ++ ) {
			ei->lc_lcz[i] = LCC_ERROR;
			ei->lc_mask[i] = LCZM_ALL;
		}
		ei->from = ei->to = 0;
	}
	return;
}

void
search_name_script_info(
	char * name,
	NAME_SCRIPT_TABLE * tbl,
	FT2_ENCODE_INFO * ei)
{
int i;
char * str;
char * p;
	for ( ; tbl->script ; tbl ++ ) {
		if ( strcmp(tbl->script,name) == 0 )
			goto ok;
		str = copy_str(tbl->script);
		for ( p = str ; *p ; p ++ ) {
			if ( 'a' <= *p && *p <= 'z' )
				*p += 'A' - 'a';
		}
		if ( strcmp(str,name) == 0 ) {
			d_f_ree(str);
			goto ok;
		}
		d_f_ree(str);
		continue;
	ok:
		for ( i = 0 ; i < 2 ; i ++ ) {
			ei->lc_lcz[i] = tbl->lcz[i];
			ei->lc_mask[i] = tbl->mask[i];
		}
		ei->from = tbl->from;
		ei->to = tbl->to;
		break;
	}
	if ( tbl->script == 0 ) {
		for ( i = 0 ; i < 2 ; i ++ ) {
			ei->lc_lcz[i] = LCC_ERROR;
			ei->lc_mask[i] = LCZM_ALL;
		}
		ei->from = ei->to = 0;
	}
	return;
}
void
get_dt_ft2_lc_encoding(FT_CharMap cm,FT2_ENCODE_INFO *ei)
{
	ei->driver_type = DT_FT2;
	search_code_script_info(cm->encoding,&ft2_encoding_table[0],ei);
}


int
get_dt_bdf_lc_encoding(FT_Face face,FT2_ENCODE_INFO *ei)
{
char * encoding;
char * registory;
int len;
	if ( FT_Get_BDF_Charset_ID(face,
			(const char**)&encoding,
			(const char**)&registory) ) {
		return -1;
	}
	ei->driver_type = DT_BDF;
	len = strlen(registory);
	registory = realloc(registory,len+strlen(encoding)+10);
	registory[len] = '-';
	strcpy(&registory[len+1],encoding);

	search_name_script_info(registory,&bdf_encoding_table[0],ei);
	free(encoding);
	free(registory);
	return 0;
}

int
get_dt_mac_lc_encoding(FT_CharMap cm,FT2_ENCODE_INFO *ei)
{
unsigned long ret;
	ret = FT_Get_CMap_Language_ID(cm);
	if ( ret == 0 )
		return -1;
	search_code_script_info(ret,&mac_script_tbl[0],ei);
	return 0;
}


typedef struct mask_statistics {
	struct mask_statistics *	next;
	L_CHAR				mask;
	L_CHAR				lcz;
	int				count;
} MASK_STATISTICS;


MASK_STATISTICS *
_insert_mask(MASK_STATISTICS * ms,MASK_STATISTICS * ms1,int count)
{
MASK_STATISTICS * ms2;
	for ( ms2 = ms ; ms2 ; ms2 = ms2->next ) {
		if ( ms2->mask != ms1->mask )
			continue;
		if ( ms2->lcz != ms1->lcz )
			continue;
		ms2->count += count;
		return ms;
	}
	ms1->next = ms;
	return ms1;
}

MASK_STATISTICS *
insert_mask(MASK_STATISTICS  * ms,FT2_ENCODE_INFO * ei,int count)
{
MASK_STATISTICS * ms1;
int i;
	for ( i = 0 ; i < 2 ; i ++ ) {
		if ( ei->lc_lcz[i] == LCC_ERROR )
			continue;
		ms1 = d_alloc(sizeof(*ms1));
		ms1->mask = ei->lc_mask[i];
		ms1->lcz = ei->lc_lcz[i];
		ms = _insert_mask(ms,ms1,count);
	}
	return ms;
}

MASK_STATISTICS *
get_max_pattern_mask(MASK_STATISTICS ** msp)
{
MASK_STATISTICS ** retp;
MASK_STATISTICS * ret;
	retp = msp;
	if ( *retp == 0 )
		return 0;
	for ( msp = &(*msp)->next ; *msp ; msp = &(*msp)->next ) {
		if ( (*retp)->count < (*msp)->count )
			retp = msp;
	}
	ret = *retp;
	*retp = (*retp)->next;
	ret->next = 0;
	return ret;
}

int
cmp_mask_pattern(MASK_STATISTICS * ms,FT2_ENCODE_INFO * ei)
{
int i;
	for ( i= 0 ; i < 2 ; i ++ ) {
		if ( ms->mask != ei->lc_mask[i] )
			continue;
		if ( ms->lcz != ei->lc_lcz[i] )
			continue;
		return 0;
	}
	return -1;
}

void
free_mask_pattern(MASK_STATISTICS * ms)
{
MASK_STATISTICS * ms1;
	for ( ; ms ; ) {
		ms1 = ms->next;
		d_f_ree(ms);
		ms = ms1;
	}
}

int
_ft2_get_font(void * a)
{
struct ft2_get_font_t *arg = (struct ft2_get_font_t*)a;
LC_FONT * font = arg->font;
//FT_Face * face = arg->face;

int err;
LC_FONT_SIZE_LIST sl;

AVT_NODE * n1;
FT2_FONTREC fr_buf;
FT2_FONTREC * fr1, * fr2;
int next_type;
int size_len;
int * size_list;
int i,j,k;
FT2_FONT_WORK * fw;
FT2_ENCODE_INFO * ei;
FT_CharMap cm;
MASK_STATISTICS * ms;
MASK_STATISTICS * ms_list[2];
int count;

	arg->font_work = 0;
	fw = d_alloc(sizeof(*fw));
	memset(fw,0,sizeof(*fw));
	fr_buf.family = get_family_from_fontname(font->fontname);
	fr_buf.style = get_style_from_fontname(font->fontname);
	if ( fr_buf.style == 0 )
		goto family_only;
	n1 = avt_search(ft2_fontrec_all_tree,&fr_buf,cmp_fontrec);
	next_type = 0;
	fw->type = FWT_ALL_LIST;
	if ( n1 == 0 ) {
		d_f_ree(fr_buf.family);
		d_f_ree(fr_buf.style);
		fr_buf.family = ll_copy_str(font->fontname);
		fr_buf.style = 0;
	family_only:
		fw->type = FWT_FAMILY_LIST;
		next_type = 1;
		n1 = avt_search(ft2_fontrec_family_tree,
				&fr_buf,cmp_fontrec_family);
		if ( n1 == 0 ) {
			d_f_ree(fr_buf.family);
			d_f_ree(fw);

			return -1;
		}
	}
	fw->fr = n1->data;
	fr2 = fr1 = n1->data;

ss_printf("font : %ls [ %s ]\n", font->fontname, fr2->path);

	size_len = 0;
	for ( ; fr1 ; fr1 = (next_type == 0 ? fr1->all_next :
					fr1->family_next) ) {
		err = FT_New_Face(ft2_library, 
			fr1->path, 
			fr1->face, 
			&fr1->work_face);
		if ( err ) {
			fr1->active = 0;
			continue;
		}
		fr1->active = 1;
		fr1->encode_info = d_alloc(sizeof(FT2_ENCODE_INFO)*
				fr1->work_face->num_charmaps);
		for ( i = 0 ; i < fr1->work_face->num_charmaps ; i ++ ) {
			ei = &fr1->encode_info[i];
			memset(ei,0,sizeof(*ei));
			cm = fr1->work_face->charmaps[i];
			if ( cm->encoding 
					!= FT_ENCODING_NONE ) {
				ei->driver_type = DT_FT2;
				get_dt_ft2_lc_encoding(cm,ei);
				continue;

//ss_printf("FACE NUM %x - %x %x\n",ei,ei->lc_lcz[0],ei->lc_mask[0]);
			}
			if ( get_dt_bdf_lc_encoding(fr1->work_face,ei)
					== 0 )
				continue;
			if ( cm->platform_id
				== TT_PLATFORM_MACINTOSH &&
				cm->encoding_id
				!= TT_MAC_ID_ROMAN ) {

				ei->driver_type = DT_MACFONT;
				get_dt_mac_lc_encoding(cm,ei);
				continue;
			}
			ei->driver_type = DT_WINFONT;
		}

		size_len += fr1->size_len;
	}
	ms = 0;
	for ( fr1 = fr2 ; fr1 ; fr1 = (next_type == 0 ? fr1->all_next :
					fr1->family_next) ) {
		if ( fr1->sizes[0] == -1 ) {
			for ( i = 0 ; i < fr1->work_face->num_charmaps ; i ++ )
				ms = insert_mask(ms,
					&fr1->encode_info[i],count);
			if ( fw->scalable == 1 )
				continue;
		        sl.from = 0;
			sl.to = 0xffff;
			sl.size = -1;
			_insert_lc_font_size_list(font,&sl);
			fw->fr = fr1;
			fw->scalable = 1;
		}
	}
	if ( fw->scalable )
		goto end;
	free_mask_pattern(ms);

	size_list = d_alloc(sizeof(int)*size_len);
	ms = 0;
	j = 0;
	for ( fr1 = fr2 ; fr1 ; fr1 = (next_type == 0 ? fr1->all_next :
					fr1->family_next) ) {
		count = 0;
		for ( k = 0 ; k < fr1->size_len ; k ++ ) {
			for ( i = 0 ; i < j ; i ++ )
				if ( fr1->sizes[k] 
						== size_list[i] )
					goto ok;
			size_list[j] = fr1->sizes[k];
			j ++;
			count ++;
		ok:	;
		}
		for ( i = 0 ; i < fr1->work_face->num_charmaps ; i ++ )
			ms = insert_mask(ms,&fr1->encode_info[i],count);
	}
	for ( i = 0 ; i < j ; i ++ ) {
	        sl.from = 0;
		sl.to = 0xffff;
		sl.size = size_list[i];
		_insert_lc_font_size_list(font,&sl);
	}
	d_f_ree(size_list);

end:
	for ( i = 0 ; i < 2 ; i ++ )
		ms_list[i] = get_max_pattern_mask(&ms);

	for ( i = 0 ; i < 2 ; i ++ ) {
		if ( ms_list[i] == 0 ) {
			font->mask[i] = LCZM_ALL;
			font->lcz[i] = LCC_ERROR;
		}
		else {
			font->mask[i] = ms_list[i]->mask;
			font->lcz[i] = ms_list[i]->lcz;
		}
ss_printf("FONT %i %x %x\n",i,font->mask[i],font->lcz[i]);
	}
	free_mask_pattern(ms);
	for ( i = 0 ; i < 2 ; i ++ )
		free_mask_pattern(ms_list[i]);

	arg->font_work = fw;

	if ( fr_buf.style )
		d_f_ree(fr_buf.style);
	if ( fr_buf.family )
		d_f_ree(fr_buf.family);
	return 0;
}


int
ft2_get_font(
	LC_FONT * font,
	LC_FONT_ENGINE * fe)
{
int er;
struct ft2_get_font_t arg = {font,fe};
	er = ft2_do(_ft2_get_font,&arg);
	if ( er )
		return er;

	insert_font_work(font,fe,arg.font_work);
	return 0;
}



struct ft2_font_coverage_t {
	FT2_FONTREC * fr;
	int size;
};


char *
ft2_get_coverage(FT2_FONTREC * fr)
{
char * c;
int cnt;
int i,j;
L_CHAR ch;
	c = d_alloc(0x10000/8);
	cnt = 0;
	for ( i = 0 ; i < 0x10000/8 ; i ++ ) {
		c[i] = 0;
		for ( j = 0 ; j < 8 ; j ++ ) {
			ch = i * 8 + j;
			if ( FT_Get_Char_Index(
				fr->work_face,
				ch& 0xffff) ) {
				c[i] |= 1<<j;
				cnt ++;
			}
		}
	}

ss_printf("FACE COVERAGE %s %x %x %x\n",fr->path,cnt,
fr->work_face->charmaps[0]->encoding,
fr->work_face->charmaps[0]->encoding_id);


	return c;
}


int
_ft2_font_coverage(void * a) {
struct ft2_font_coverage_t *arg = (struct ft2_font_coverage_t*)a;
FT2_FONTREC * fr;
int err;
int i;

	fr = arg->fr;
	for ( i = 0 ; i < fr->work_face->num_charmaps ; i ++ ) {
		if ( fr->encode_info[i].coverage )
			continue;
		err = FT_Select_Charmap(fr->work_face, 
			fr->work_face->charmaps[i]->encoding);
		if ( err )
			er_panic(">>>");
		fr->encode_info[i].coverage = ft2_get_coverage(fr);
		
	}
	return 0;
}

int
ft2_font_coverage(LC_FONT *f, int size, L_CHAR ch)
{
int i,j,k;
FT2_FONT_WORK * fw;
FT2_FONTREC * fr;
struct ft2_font_coverage_t arg;
int ch_i,ch_j;
char * c;
FT2_ENCODE_INFO * ei;
	for ( i = 0 ; i < f->fw_len ; i ++ )
		if ( f->fw_list[i].fe->type == &ft2_font_engine_type )
			break;
	if ( f->fw_len <= i )
		return -1;
	fw = (FT2_FONT_WORK*)f->fw_list[i].work;

	for ( fr = fw->fr ; fr ;
		fr = (fw->type == FWT_ALL_LIST ?
			fr->all_next :
			fr->family_next) ) {
		for ( i = 0 ; i < fr->size_len ; i ++ ) {
			if ( fw->scalable == 0 ) {
				if ( fr->sizes[i] != size )
					continue;
			}
			else {
				if ( fr->sizes[0] != -1 )
					continue;
			}
			ei = &fr->encode_info[0];
			if ( ei->coverage == 0 ) {
				arg.fr = fr;
				ft2_do(
					_ft2_font_coverage,
					&arg);
			}
			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_mask[k] & ch)
						== ei->lc_lcz[k] )
						break;
				}
				if ( k == 2 )
					continue;
				ch_i = (ch&(~ei->lc_mask[k]))/8;
				ch_j = (ch&(~ei->lc_mask[k]))%8;
				c = ei->coverage;
				if ( c[ch_i] 
					& (1<<ch_j) )
					return 0;
				else return -1;
			}
		}
	}
	return -1;
}
