/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.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	"task.h"
#include	"utils.h"
#include	"memory_debug.h"
#include	"lock_level.h"
#include	"long_char.h"
#include	"lc_encode.h"

SEM wct_lock;
LC_WRITING_STYLE * ws_list;
LC_TABLE * lc_table_list;

void _free_lc_cws(LC_CWS);
LC_COND_WRITING_STYLE * __get_cws(LC_WRITING_STYLE * ws,LC_WS_COND * cond);
int
_next_cws_list(LC_CWS * cws,LC_COND_WRITING_STYLE_LIST ** cp);


void tick_lc_table();

void
init_wct()
{
	wct_lock = new_lock(LL_WCT);
	new_tick(tick_lc_table,10,0);
}


/*
LC_WRITING_STYLE *
_new_writing_style(L_CHAR * stylename)
{
LC_WRITING_STYLE * ws;
	ws = d_alloc(sizeof(*ws));
	ws->stylename = ll_copy_str(stylename);
	ws->next = ws_list;
	ws_list = ws->next;
	return ws;
}

LC_WRITING_STYLE *
new_writing_style(L_CHAR * stylename)
{
LC_WRITING_STYLE * ws;
	lock_task(wct_lock);
	ws = _new_writing_style(stylename);
	unlock_task(wct_lock,"new_writing_style");
	return ws;
}
*/

WRITABLE_CODE_TABLE *
_search_w(WCT * t,L_CHAR lcz)
{
WRITABLE_CODE_TABLE * ret;
	for ( ret = t->list ; ret ; ret = ret->next )
		if ( ret->lcz == lcz )
			return ret;
	return 0;
}

void
_write_char_w(
	WRITABLE_CODE_TABLE * w,
	L_CHAR ch,
	LC_FONT * f,
	int f_size,
	L_CHAR after)
{
LC_TABLE * tbl;
LCF_SET * s;
int size;
int i;

	/* lc_table */

	size = ch&(~w->mask);
	tbl = w->lc_tbl;
	if ( tbl->ref == 1 ) {
		tbl = w->lc_tbl;
		if ( tbl->size <= size ) {
			size = - w->mask;
			goto copy;
		}
	}
	else {
		if ( tbl->size <= size )
			size = - w->mask;
		else	size = tbl->size;
	copy:
		s = d_alloc(sizeof(LCF_SET)*size);
		memcpy(s,w->lc_tbl->tbl,sizeof(LCF_SET)*tbl->size);
		for ( i = tbl->size ; i < size ; i ++ ) {
			s[i].font = 0;
			s[i].ch = 0;
			s[i].size = 0;
		}
		if ( tbl->ref == 1 ) {
			d_f_ree(tbl->tbl);
			tbl->tbl = s;
			tbl->size = size;
		}
		else {
			tbl->ref --;

			tbl = d_alloc(sizeof(LC_TABLE));
			tbl->flags = LCTF_NO_CHECK;
			tbl->relate = 0;
			tbl->tbl = s;
			tbl->ref = 1;
			tbl->size = size;
			w->lc_tbl = tbl;
			tbl->next = lc_table_list;
			lc_table_list = tbl;
		}
	}
	s = &tbl->tbl[ch&(~w->mask)];
	s->font = f;
	s->size = f_size;
	s->ch = after;
}


int
_search_ws_font(LCF_SET * sp,FONT_PRI * fp,L_CHAR ch,int size)
{
L_CHAR after;
LC_FONT_SIZE_LIST * sl;
int mask;
int i;
int cov_size;


	for ( ; fp->font ; fp ++ ) {
		after = LCC_ERROR;
		for ( i = 0 ; i < 2 ; i ++ ) {
			if ( fp->font->lcz[i] == LCC_ERROR )
				continue;
			after = code_convert(ch,fp->font->lcz[i]);
			if ( after != LCC_ERROR )
				break;
		}
		if ( after == LCC_ERROR )
			continue;
		for ( i = 0 ; i < 2 ; i ++ ) {
			if ( (after & fp->font->mask[i]) 
				== (fp->font->lcz[i] & fp->font->mask[i]) )
				goto ok;
		}
		continue;
	ok:
		sl = _get_lc_font_size_list(fp->font,size,fp->method);
		if ( sl == 0 )
			continue;
		mask = get_lc_mask(after);
		if ( (((int)sl->from)&(~mask)) > (((int)after)&(~mask)) )
			continue;
		if ( (((int)sl->to)&(~mask)) < (((int)after)&(~mask)) )
			continue;
		if ( fp->font->fw_list[0].fe->type->fe_coverage == 0 )
			goto ok2;
		if ( sl->size == -1 )
			cov_size = size;
		else	cov_size = sl->size;
		if ( (*fp->font->fw_list[0].fe->type->fe_coverage)
				(fp->font,cov_size,after) != 0 )
			continue;
	ok2:
		sp->ch = after;
		sp->font = fp->font;
		if ( sl->size == -1 )
			sp->size = size;
		else	sp->size = sl->size;
		return 0;
	}
	return -1;
}


void
_wct_setup_char(FONT_PRI * fp,WRITABLE_CODE_TABLE * w,L_CHAR ch,int size)
{
LCF_SET set;
	if ( _search_ws_font(&set,fp,ch,size) < 0 )
		return;
	_write_char_w(w,ch,set.font,set.size,set.ch);
}

FONT_PRI *
fitting_font_pri(LC_COND_WRITING_STYLE * cws,L_CHAR lcz)
{
FONT_PRI * fp;
char * locale;
	switch ( lcz ) {
	case 0:
	case LCZ_2BC_UNICODE_v3_0_UN:
	case LCZ_2BC_UNICODE_v2_0_UN:
	case LCZ_2BC_UNICODE_v1_1_UN:
	default:
		locale = get_locale();
		if ( strcmp(locale,"ja") == 0 )
			fp = cws->ja_fonts;
		else if ( strcmp(locale,"tw") == 0 )
			fp = cws->tw_fonts;
		else if ( strcmp(locale,"cn") == 0 )
			fp = cws->cn_fonts;
		else if ( strcmp(locale,"kr") == 0 )
			fp = cws->kr_fonts;
		else	fp = cws->un_fonts;
		if ( fp == 0 )
			fp = cws->un_fonts;
		if ( fp == 0 )
			return 0;
		break;
	case LCZ_2BC_UNICODE_v3_0_JP:
	case LCZ_2BC_UNICODE_v2_0_JP:
	case LCZ_2BC_UNICODE_v1_1_JP:
		fp = cws->ja_fonts;
		if ( fp == 0 ) 
			return 0;
		break;
	case LCZ_2BC_UNICODE_v3_0_TW:
	case LCZ_2BC_UNICODE_v2_0_TW:
	case LCZ_2BC_UNICODE_v1_1_TW:
		fp = cws->tw_fonts;
		if ( fp == 0 )
			return 0;
		break;
	case LCZ_2BC_UNICODE_v3_0_KR:
	case LCZ_2BC_UNICODE_v2_0_KR:
	case LCZ_2BC_UNICODE_v1_1_KR:
		fp = cws->kr_fonts;
		if ( fp == 0 )
			return 0;
		break;
	case LCZ_2BC_UNICODE_v3_0_CN:
	case LCZ_2BC_UNICODE_v2_0_CN:
	case LCZ_2BC_UNICODE_v1_1_CN:
		fp = cws->cn_fonts;
		if ( fp == 0 )
			return 0;
		break;
	}
	return fp;
}


FONT_PRI *
merge_fp_list(FONT_PRI * fp,FONT_PRI * fp2)
{
int len,len2;
FONT_PRI * _fp;

	if ( fp == 0 ) {
		for ( len = 0 , _fp = fp2 ; _fp->font ; _fp ++ , len ++ );
		fp = d_alloc((len+1)*sizeof(FONT_PRI));
		memcpy(fp,fp2,(len+1)*sizeof(FONT_PRI));
		return fp;
	}
	for ( len = 0 , _fp = fp ; _fp->font ; _fp ++ , len ++ );
	for ( len2 = 0 , _fp = fp2 ; _fp->font ; _fp ++ , len2 ++ );
	fp = d_re_alloc(fp,(len+len2+1)*sizeof(FONT_PRI));
	memcpy(&fp[len],fp2,(len2+1)*sizeof(FONT_PRI));
	return fp;
}

FONT_PRI *
cws_fitting_font_pri(LC_CWS * cws,L_CHAR lcz,int * fp_ws_count)
{
LC_COND_WRITING_STYLE_LIST ** cp;
FONT_PRI * fp,* _fp;
int cnt;


	cnt = *fp_ws_count;
	fp = 0;
	cp = &cws->list;
retry:


	for ( ; *cp ; cp = &(*cp)->next ) {
		_fp = fitting_font_pri((*cp)->cws,lcz);
		if ( _fp ) {
			fp = merge_fp_list(fp,_fp);
			if ( fp->font == 0 ) {
				d_f_ree(fp);
				fp = 0;
			}
		}
	}
	if ( fp && *fp_ws_count > 0 && cnt <= 0 )
		return fp;
	if ( _next_cws_list(cws,cp) < 0 ) {
		*fp_ws_count = -1;
		return fp;
	}
	if ( *fp_ws_count > 0 )
		cnt --;
	goto retry;
}

WRITABLE_CODE_TABLE *
_wct_setup(LC_COND_WRITING_STYLE * cws,L_CHAR ch_input,int f_size)
{
WRITABLE_CODE_TABLE * w;
LC_TABLE * tbl;
LCF_SET * s;
L_CHAR ch;
FONT_PRI * fp;
L_CHAR mask,lcz;
int size;

	mask = get_lc_mask(ch_input);
	if ( mask == - mask )
		mask = 0xffff0000;
	else if ( - (int)mask > 0x10000 )
		mask = 0xffff0000;
	lcz = ch_input & mask;

	w = d_alloc(sizeof(*w));
	w->lcz = lcz;
	w->mask = mask;
	size = - w->mask;
	w->lc_tbl = 0;

	fp = fitting_font_pri(cws,lcz);
	if ( fp == 0 )
		return w;

	tbl = w->lc_tbl = d_alloc(sizeof(LC_TABLE));
	tbl->flags = LCTF_NO_CHECK;
	tbl->relate = 0;
	tbl->ref = 1;
	tbl->size = size;
	tbl->tbl = d_alloc(sizeof(LCF_SET)*size);
	s = tbl->tbl;
	for ( ; size ; size -- , s ++ ) {
		s->ch = LCC_ERROR;
		s->font = 0;
		s->size = 0;
	}
	tbl->next = lc_table_list;
	lc_table_list = tbl;

	mask = w->mask;
	for ( ch = lcz ; (ch & mask) == lcz ; ch ++ )
		_wct_setup_char(fp,w,ch,f_size);
	return w;
}

int
_wct_check_scalable_fonts(WCT * t)
{
WRITABLE_CODE_TABLE * w;
int i;
LC_TABLE * tbl;
LCF_SET * s;
	for ( w = t->list ; w ; w = w->next ) {
		tbl = w->lc_tbl;
		if ( tbl ) {
			s = tbl->tbl;
			for ( i = tbl->size ; i > 0 ; i -- , s ++ ) {
				if ( s->font == 0 )
					continue;
				if ( s->font->list->size >= 0 )
					return -1;
			}
		}
	}
	return 0;
}



WCT *
_get_wct(LC_COND_WRITING_STYLE * cws,int size)
{
WCT * ret;
	for ( ret = cws->wct ; ret ; ret = ret->next ) {
		if ( ret->size < 0 )
			return ret;
		if ( ret->size == size )
			return ret;
	}
	ret = d_alloc(sizeof(*ret));
	ret->list = 0;
	if ( cws->wct )
		ret->size = size;
	else	ret->size = -1;
	ret->next = cws->wct;
	cws->wct = ret;
	return ret;
}

LC_COND_WRITING_STYLE_LIST *
copy_cws(LC_COND_WRITING_STYLE_LIST * l)
{
LC_COND_WRITING_STYLE_LIST * ret;
	ret = d_alloc(sizeof(*ret));
	*ret = *l;
	ret->next = 0;
	return ret;
}

void
cws_stack_push(LC_CWS * cws,LC_WS_CHILDREN_LIST * ws_cl)
{
LC_COND_WRITING_STYLE_LIST * s;
	s = d_alloc(sizeof(*s));
	s->ws_cl = ws_cl;
	s->next = cws->stack;
	s->cws = 0;
	cws->stack = s;
}

void
cws_stack_pop(LC_CWS * cws)
{
LC_COND_WRITING_STYLE_LIST * s;
	if ( cws->stack == 0 )
		return;
	s = cws->stack;
	cws->stack = s->next;
	d_f_ree(s);
}

void
cws_stack_move(LC_CWS * cws)
{
LC_COND_WRITING_STYLE_LIST * s;
	if ( cws->stack == 0 )
		return;
	s = cws->stack;
	if ( s->ws_cl->next == 0 )
		return;
	s->ws_cl = s->ws_cl->next;
	s->cws = 0;
}


LCF_SET
__wct_convert(LC_COND_WRITING_STYLE * cws,WCT * wct,L_CHAR ch,int size)
{
WRITABLE_CODE_TABLE * t;
LCF_SET ret;
LC_TABLE * tbl;
unsigned int _ch;

retry:
	for ( t = wct->list ; t ; t = t->next ) {
		if ( (t->lcz & t->mask) != (ch & t->mask) )
			continue;
		tbl = t->lc_tbl;
		if ( tbl == 0 ) {
			ret.ch = LCC_ERROR;
			ret.font = 0;
			return ret;
		}
		_ch = (unsigned int)(ch & ~(t->mask));
		if ( _ch >= tbl->size )
			break;
		ret = tbl->tbl[_ch];
		if ( ret.ch == 0 )
			ret.ch = LCC_ERROR;
		return ret;
	}
	t = _wct_setup(cws,ch,size);
	t->next = wct->list;
	wct->list = t;
	
	if ( wct->size < 0 && _wct_check_scalable_fonts(wct) )
		wct->size = size;
	
	goto retry;
}


int
_next_cws_list(LC_CWS * cws,LC_COND_WRITING_STYLE_LIST ** cp)
{
	if ( cws->stack == 0 )
		return -1;
	cws->stack->cws = __get_cws(cws->stack->ws_cl->ws,&cws->cond);
	*cp = copy_cws(cws->stack);
	if ( cws->stack->ws_cl->ws->children )
		cws_stack_push(cws,cws->stack->ws_cl->ws->children);
	else {
		for ( ; cws->stack && cws->stack->ws_cl->next == 0 ; )
			cws_stack_pop(cws);
		if ( cws->stack == 0 )
			return 0;
		cws_stack_move(cws);
	}
	return 0;
}

LCF_SET
_wct_convert(LC_CWS * cws,L_CHAR ch,int size)
{
WCT * wct;
LCF_SET ret;
LC_COND_WRITING_STYLE_LIST ** cp;

	cp = &cws->list;
retry:
	for ( ; *cp ; cp = &(*cp)->next ) {
		wct = _get_wct((*cp)->cws,size);
		ret = __wct_convert((*cp)->cws,wct,ch,size);
		if ( ret.ch != LCC_ERROR )
			return ret;
	}
	if ( _next_cws_list(cws,cp) < 0 )
		return ret;

	goto retry;
}

void
check_str(L_CHAR * str,int len)
{
	for ( ; len ; str ++ , len -- )
		if ( *str == 0x79797979 )
			er_panic("check_str");
}

LC_CWS
_get_cws(LC_WRITING_STYLE *,LC_WS_COND*);

LCF_SET *
_ws_convert(
	LC_WRITING_STYLE * ws,
	LC_WS_COND * cond,
	int size,
	L_CHAR * str_input,
	int length,
	int flags)
{
LCF_SET * ret,* _ret;
int len;
int p;
LCF_SET ls;
char buf[15];
int _len,i;
L_CHAR ch;
int cnt;
L_CHAR * str, * str_org;
LC_CWS cws;



	cws = _get_cws(ws,cond);


	if ( (flags & CBF_SRC_COMBINE) ) {
		str_org = str = str_input;
		len = length;
	}
	else {
		str_org = str = combine(str_input,length);
		len = l_strlen(str);
	}
	ret = d_alloc(sizeof(LCF_SET)*(len+1));
	cnt = length;
	for ( p =  0 ; cnt ; str ++ , cnt -- ) {
		ls = _wct_convert(&cws,*str,size);
		if  ( ls.ch == LCC_ERROR ) {
ss_printf("CONVERT ERROR\n");
			sprintf(buf,"&cx%x;",
				(int)*str);
			_len = strlen(buf);
			len += _len;
			ret = d_re_alloc(ret,sizeof(LCF_SET)*(len+1));
			for ( i = 0 ; i < _len ; i ++ ) {
				ch = buf[i];
				ret[p++] = _wct_convert(&cws,ch,size);
			}
		}
		else {
			ret[p++] = ls;
		}
	}
	ret[p].ch = 0;
	ret[p].font = 0;

	_free_lc_cws(cws);

	if ( str_org != str_input )
		d_f_ree(str_org);
	if ( flags & CBF_DST_COMBINE )
		return ret;
	else {
		_ret = uncombine_lcf(ret,p);
		d_f_ree(ret);
		return _ret;
	}
}


LCF_SET *
ws_convert(
	LC_WRITING_STYLE * ws,
	LC_WS_COND * cond,
	int size,
	L_CHAR * str,
	int length,
	int flags)
{
LCF_SET * ret;
	lock_task(wct_lock);
	ret = _ws_convert(ws,cond,size,str,length,flags);
	unlock_task(wct_lock,"ws_convert");
	return ret;
}


int
fpn_len(FONT_PRI_NAME * n)
{
int ret;
	for ( ret = 0 ; n->font ; n ++ , ret ++ );
	return ret;
}

void
get_font_pri(FONT_PRI ** fp,FONT_PRI_NAME * fpn,LC_WS_COND * cond)
{
int len;
FONT_PRI * fpp;

	if ( fpn == 0 ) {
		*fp = 0;
		return;
	}
	len = fpn_len(fpn);
	fpp = *fp = d_alloc(sizeof(FONT_PRI)*(len+1));
	for ( ; fpn->font ; fpn ++ ) {
		if ( check_font_name_and_cond(cond,fpn->font) < 0 )
			continue;
		fpp->font = _lc_new_font(fpn->font,fpn->lcz,fpn->mask);

		if ( fpp->font == 0 )
			continue;
		if ( check_font_and_cond(cond,fpp->font) < 0 ) {
			fpp->font = 0;
			continue;
		}
		fpp->method = fpn->method;
		fpp ++;
	}

	fpp->font = 0;
	fpp->method = 0;

}


int
_lc_cmp_ws_cond(LC_WS_COND * c1,LC_WS_COND * c2)
{
LC_FONT_ENGINE ** fe1, ** fe2;
LC_FONT_ENGINE_TYPE ** fet1, ** fet2;
	if ( c1->cond != c2->cond )
		return -1;
	if ( c1->cond & WSC_FE_LIST ) {
		for ( fe1 = c1->fe , fe2 = c2->fe ;
				*fe1 && *fe2 ; fe1 ++ , fe2 ++ )
			if ( *fe1 != *fe2 )
				return -1;
		if ( *fe1 != *fe2 )
			return -1;
	}
	if ( c1->cond & WSC_FET_LIST ) {
		for ( fet1 = c1->fet , fet2 = c2->fet ;
				*fet1 && *fet2 ; fet1 ++ , fet2 ++ )
			if ( *fet1 != *fet2 )
				return -1;
		if ( *fet1 != *fet2 )
			return -1;
	}
	return 0;
}

void
_lc_copy_ws_cond(LC_WS_COND * dest,LC_WS_COND * src)
{
int len;
	*dest = *src;
	if ( dest->cond & WSC_FE_LIST ) {
		for ( len = 0 ; src->fe[len] ; len ++ );
		dest->fe = d_alloc(sizeof(LC_FONT_ENGINE*)*(len+1));
		memcpy(dest->fe,src->fe,
			sizeof(LC_FONT_ENGINE*)*(len+1));
	}
	if ( dest->cond & WSC_FET_LIST ) {
		for ( len = 0 ; src->fet[len] ; len ++ );
		dest->fet = d_alloc(sizeof(LC_FONT_ENGINE_TYPE*)*(len+1));
		memcpy(dest->fet,src->fet,
			sizeof(LC_FONT_ENGINE_TYPE*)*(len+1));
	}
}

void
_lc_free_ws_cond(LC_WS_COND * c)
{
	if ( c->cond & WSC_FE_LIST ) {
		d_f_ree(c->fe);
	}
	if ( c->cond & WSC_FET_LIST ) {
		d_f_ree(c->fet);
	}
}

LC_COND_WRITING_STYLE * __get_cws(LC_WRITING_STYLE * ws,LC_WS_COND * cond)
{
LC_COND_WRITING_STYLE * cws;


	for ( cws = ws->cws_list ; cws ; cws = cws->next )
		if ( _lc_cmp_ws_cond(&cws->c,cond) == 0 )
			return cws;
	cws = d_alloc(sizeof(*cws));
	memset(cws,0,sizeof(*cws));
	_lc_copy_ws_cond(&cws->c,cond);
	get_font_pri(&cws->un_fonts,ws->un_fonts_n,cond);
	get_font_pri(&cws->ja_fonts,ws->ja_fonts_n,cond);
	get_font_pri(&cws->tw_fonts,ws->tw_fonts_n,cond);
	get_font_pri(&cws->cn_fonts,ws->cn_fonts_n,cond);
	get_font_pri(&cws->kr_fonts,ws->kr_fonts_n,cond);
	cws->next = ws->cws_list;
	ws->cws_list = cws;
	return cws;
}

void
_free_lc_cws(LC_CWS cws)
{
LC_COND_WRITING_STYLE_LIST * lst;
	for ( ; cws.list ; ) {
		lst = cws.list;
		cws.list = lst->next;
		d_f_ree(lst);
	}
	_lc_free_ws_cond(&cws.cond);
}

LC_CWS
_get_cws(LC_WRITING_STYLE * ws,LC_WS_COND * cond)
{
LC_CWS ret;
	ret.list = 0;
	ret.top = ws;
	ret.stack = 0;
	cws_stack_push(&ret,ws->self_children_list);
	_lc_copy_ws_cond(&ret.cond,cond);
	return ret;
}

LC_CWS get_cws(LC_WRITING_STYLE * ws,LC_WS_COND * cond)
{
LC_CWS ret;
	lock_task(wct_lock);
	ret = _get_cws(ws,cond);
	unlock_task(wct_lock,"get_cws");
	return ret;
}

FONT_PRI_NAME *
copy_font_pri_name(FONT_PRI_NAME * f)
{
FONT_PRI_NAME * ret;
int len;
int i;
	if ( f == 0 )
		return 0;
	len = fpn_len(f);
	ret = d_alloc(sizeof(*ret)*(len+1));
	memcpy(ret,f,sizeof(*ret)*(len+1));
	for ( i = 0 ; i < len ; i ++ )
		ret[i].font = ll_copy_str(ret[i].font);
	ret[len].font = 0;
	return ret;
}



LC_WRITING_STYLE *
_new_ws(
	L_CHAR * stylename,
	FONT_PRI_NAME * un,
	FONT_PRI_NAME * ja,
	FONT_PRI_NAME * tw,
	FONT_PRI_NAME * cn,
	FONT_PRI_NAME * kr
	)
{
LC_WRITING_STYLE * ws;
LC_WS_CHILDREN_LIST * cl;
	ws = d_alloc(sizeof(*ws));
	ws->stylename = ll_copy_str(stylename);
	ws->children = 0;
	ws->cws_list = 0;
	ws->self_children_list = cl = d_alloc(sizeof(*cl));
	cl->next = 0;
	cl->ws = ws;
/*
	ws->un_fonts = 0;
	ws->ja_fonts = 0;
	ws->tw_fonts = 0;
	ws->cn_fonts = 0;
	ws->kr_fonts = 0;
*/
	ws->un_fonts_n = copy_font_pri_name(un);
	ws->ja_fonts_n = copy_font_pri_name(ja);
	ws->tw_fonts_n = copy_font_pri_name(tw);
	ws->cn_fonts_n = copy_font_pri_name(cn);
	ws->kr_fonts_n = copy_font_pri_name(kr);
	ws->next = ws_list;
	ws_list = ws;
	return ws;
}

LC_WRITING_STYLE *
new_ws(
	L_CHAR * stylename,
	FONT_PRI_NAME * un,
	FONT_PRI_NAME * ja,
	FONT_PRI_NAME * tw,
	FONT_PRI_NAME * cn,
	FONT_PRI_NAME * kr
	)
{
LC_WRITING_STYLE * ret;
	lock_task(wct_lock);
	ret = _new_ws(stylename,un,ja,tw,cn,kr);
	unlock_task(wct_lock,"new_ws");
	return ret;
}

LC_WRITING_STYLE *
_get_ws(L_CHAR * stylename)
{
LC_WRITING_STYLE * ws;
	for ( ws = ws_list ; ws ; ws = ws->next )
		if ( l_strcmp(ws->stylename,stylename) == 0 )
			return ws;
	return 0;
}

LC_WRITING_STYLE *
get_ws(L_CHAR * stylename)
{
LC_WRITING_STYLE * ret;

	lock_task(wct_lock);
	ret = _get_ws(stylename);
	unlock_task(wct_lock,"get_ws");
	return ret;
}

int
_set_ws_child(LC_WRITING_STYLE * ws1,LC_WRITING_STYLE * ws2)
{
LC_WS_CHILDREN_LIST * cl, ** clp;
	cl = d_alloc(sizeof(*cl));
	cl->ws = ws2;
	cl->next = 0;
	for ( clp = &ws1->children ; *clp ; clp = &(*clp)->next );
	*clp = cl;
	return 0;
}

int
set_ws_child(LC_WRITING_STYLE * ws1,LC_WRITING_STYLE * ws2)
{
int ret;
	lock_task(wct_lock);
	ret = _set_ws_child(ws1,ws2);
	unlock_task(wct_lock,"set_ws_child");
	return ret;
}

void
_gc_lc_table()
{
int flag;
LC_TABLE * t, * t1, ** tp;
LC_WRITING_STYLE * ws;
LC_COND_WRITING_STYLE * cws;
WRITABLE_CODE_TABLE * w;
WCT * wct;
	flag = 0;
	for ( t = lc_table_list ; t ; t = t->next ) {
		if ( (t->flags & LCTF_NO_CHECK) == 0 )
			break;
		for ( t1 = t->next ; t1 ; t1 = t1->next ) {
			if ( t->size != t1->size )
				continue;
			if ( t->tbl == 0 && t1->tbl )
				continue;
			if ( t->tbl && t1->tbl == 0 )
				continue;
			if ( t->tbl && t1->tbl ) {
				if ( memcmp(t->tbl,t1->tbl,
						t->size*sizeof(LCF_SET)) )
					continue;
			}
			t->relate = t1;
			flag = 1;
			break;
		}
		t->flags = 0;
	}
	if ( flag == 0 )
		return;
	for ( ws = ws_list ; ws ; ws = ws->next ) {
		for ( cws = ws->cws_list ; cws ; cws = cws->next )
			for ( wct = cws->wct ; wct ; wct = wct->next )
				for ( w = wct->list ; w ; w = w->next ) {
					if ( w->lc_tbl == 0 )
						continue;
					if ( w->lc_tbl->relate == 0 )
						continue;
					for ( t = w->lc_tbl ; t->relate ;
							t = t->relate );
					w->lc_tbl = t;
					t->ref ++;
				}
	}
	for ( tp = &lc_table_list ; *tp ; ) {
		t = *tp;
		if ( t->relate == 0 ) {
			tp = &t->next;
			continue;
		}
		if ( t->tbl )
			d_f_ree(t->tbl);
		*tp = t->next;
		d_f_ree(t);
	}
}

void
tick_lc_table()
{
	lock_task(wct_lock);
	_gc_lc_table();
	unlock_task(wct_lock,"tick_lc_table");
}



L_CHAR * copy_out_lcf_set(LCF_SET * s,int len,int flags)
{
L_CHAR * ret,* p,* _ret;
int cnt;
	ret = d_alloc(sizeof(L_CHAR)*(len+1));
	p = ret;
	for ( cnt = len ; cnt ; cnt -- , s ++ , p ++ )
		*p = s->ch;
	*p = 0;
	switch ( flags & (CBF_SRC_COMBINE|CBF_DST_COMBINE) ) {
	case CBF_SRC_COMBINE:
		_ret = uncombine(ret,len);
		d_f_ree(ret);
		return _ret;
	case CBF_DST_COMBINE:
		_ret = combine(ret,len);
		d_f_ree(ret);
		return _ret;
	default:
		return ret;
	}
}

LC_COMBINATION *
cb_mutch(int * mlen,L_CHAR * str,int maxlen)
{
extern int node_count;
extern LC_COMBINATION leaf_to_node[];
int low,high,mid,cmplen;
LC_COMBINATION * ret;
LC_COMBINATION * cb;
int i;
int max_cmplen;
	cmplen = 1;
	ret = 0;
	max_cmplen = maxlen;
	for ( low = 0 , high = node_count ;
			low + 1 < high &&
			cmplen < max_cmplen ; ) {
		mid = (low + high)/2;
		cb = &leaf_to_node[mid];
		for ( i = 0 ; i < cmplen ; i ++ ) {
			if ( str[i] < cb->leaf[i] )
				goto str_small;
			if ( str[i] > cb->leaf[i] )
				goto str_large;
		}
		if ( cb->leaf[cmplen-1] == 0 ) {
			ret = cb;
			cmplen ++;
			continue;
		}
	str_small:
		high = mid;
		continue;
	str_large:
		low = mid+1;
		continue;
	}
	if ( ret == 0 )
		return 0;
	*mlen = l_strlen(ret->leaf);
	return ret;
}

LC_CN *
cn_mutch(L_CHAR node)
{
int i;
extern LC_CN cn_lcz_sort[];
extern int cn_len;
int mask;
	mask = get_lc_mask(node);
	for ( i = 0 ; i < cn_len ; i ++ ) {
		if ( (cn_lcz_sort[i].cn & mask) == 
				(node & mask) )
			return &cn_lcz_sort[i];
	}
	return 0;
}

L_CHAR *
combine(L_CHAR * str,int len)
{
int p,q;
L_CHAR * ret;
LC_COMBINATION * cb;
int mlen;
	ret = d_alloc(sizeof(L_CHAR)*(len+1));
	for ( p = q = 0 ; str[p] && p < len ; ) {
		cb = cb_mutch(&mlen,&str[p],len - p);
		if ( cb == 0 ) {
			ret[q++] = str[p++];
		}
		else {
			ret[q++] = cb->node;
			p += mlen;
		}
	}
	ret[q] = 0;
	return ret;
}


LC_COMBINATION *
cb_mutch_node(L_CHAR node)
{
extern int node_count;
extern LC_COMBINATION node_to_leaf[];
int low,high,mid;
LC_COMBINATION * ret;
	low = 0;
	high = node_count;
	for ( ; low  < high ; ) {
		if ( low + 1 == high )
			mid = low;
		else	mid = (low + high)/2;
		ret = &node_to_leaf[mid];
		if ( ret->node == node )
			return ret;
		if ( ((int)ret->node) < ((int)node) ) {
			low = mid + 1;
			continue;
		}
		high = mid;
	}
	return 0;
}

L_CHAR *
uncombine(L_CHAR * str,int length)
{
L_CHAR * ret;
int ret_len,len;
int p,q;
LC_CN * cn;
LC_COMBINATION * cb;
	ret_len = length;
	ret = d_alloc(sizeof(*ret)*(length+1));
	for ( p = q = 0 ; str[p] && p < length ; p ++ ) {
		cn = cn_mutch(str[p]);
		if ( cn == 0 ) {
			ret[q++] = str[p];
		}
		else {
			cb = cb_mutch_node(str[p]);
			if ( cb == 0 ) {
				ret[q++] = str[p];
			}
			else {
				len = l_strlen(cb->leaf);
				ret_len += len-1;
				ret = d_re_alloc(ret,
					sizeof(L_CHAR)*(ret_len+1));
				memcpy(&ret[q],cb->leaf,
					sizeof(L_CHAR)*len);
				q += len;
			}
		}
	}
	ret[q] = 0;
	return ret;
}



LCF_SET *
uncombine_lcf(LCF_SET * set,int length)
{
LCF_SET * ret;
int ret_len,len;
int p,q,r;
LC_CN * cn;
LC_COMBINATION * cb;

	ret_len = length;
	ret = d_alloc(sizeof(*ret)*(length+1));
	for ( p = q = 0 ; set[p].ch && p < length ; p ++ ) {
		cn = cn_mutch(set[p].ch);
		if ( cn == 0 ) {
			ret[q++] = set[p];
		}
		else {
			cb = cb_mutch_node(set[p].ch);
			if ( cb == 0 ) {
				ret[q++] = set[p];
			}
			else {
				len = l_strlen(cb->leaf);
				ret_len += len-1;
				ret = d_re_alloc(ret,
					sizeof(*ret)*(ret_len+1));
				for ( r = 0 ; r < len ; q ++ , r ++ ) {
					ret[q] = set[p];
					ret[q].ch = cb->leaf[r];
				}
			}
		}
	}
	ret[q].ch = 0;
	ret[q].font = 0;
	ret[q].size = 0;
	return ret;
}

L_CHAR *
code_convert_with_combine(L_CHAR * str_input,int length,
	LCZ_SET * set,int flags)
{
LCZ_SET * set_node;
int len;
LCZ_SET * p, * q;
LC_CN * cn;
L_CHAR * str, * ret, * pp, * qq, * _ret;
int i;
	for ( len = 0 ; set[len].lcz != LCC_ERROR && len < length ; len ++ );
	set_node = d_alloc(sizeof(*set_node)*(len+1));
	i = 0;
	for ( p = set, q = set_node ; i < len ; p ++, i ++  ) {
		cn = cn_mutch(p->lcz);
		if ( cn == 0 )
			continue;
		q->lcz = cn->cn;
		q->mask = get_lc_mask(q->lcz);
		q ++;
	}
	q->lcz = LCC_ERROR;
	q->mask = 0;

	if ( flags & CBF_SRC_COMBINE ) {
		str = str_input;
		len = length;
	}
	else {
	 	str = combine(str_input,length);
		len = l_strlen(str);
	}
	ret = d_alloc(sizeof(L_CHAR)*(length+1));

	for ( pp = str , qq = ret ; *pp && len ; len -- , pp ++ , qq ++ ) {
		*qq = code_convert_set(*pp,set,set_node);
		if ( *qq == LCC_ERROR && (flags & CBF_ERR_KEEP) )
			*qq = *pp;
	}
	d_f_ree(set_node);
	*qq = 0;

	if ( str != str_input )
		d_f_ree(str);
	if ( flags & CBF_DST_COMBINE )
		return ret;
	else {
		_ret = uncombine(ret,l_strlen(ret));
		d_f_ree(ret);
		return _ret;
	}
}


char *
code_convert_str(char * str,CODE_METHOD * from,CODE_METHOD * to,int flags)
{
L_CHAR * target, * _target;
char * ret;
	target = l_string(from,str);
	_target = code_convert_with_combine(
			target,
			l_strlen(target),
			to->main_code,
			CBF_SRC_PLANE|CBF_DST_PLANE|flags);
	ret = copy_str(n_string(to,_target));
	d_f_ree(_target);
	return ret;
}


char *
code_convert_lc2str(L_CHAR * target,CODE_METHOD * to,int flags)
{
L_CHAR * _target;
char * ret;
	_target = code_convert_with_combine(
			target,
			l_strlen(target),
			to->main_code,
			CBF_SRC_PLANE|CBF_DST_PLANE|flags);
	ret = copy_str(n_string(to,_target));
	d_f_ree(_target);
	return ret;
}


typedef struct lc_select_font_list {
	struct lc_select_font_list *	next;
	LC_FONT *			font;
	int				size;
	int				count;
	int				pri;
} LC_SELECT_FONT_LIST;

LC_SELECT_FONT_LIST *
insert_select_list(LC_SELECT_FONT_LIST * sf,LC_FONT * f,int size,int pri);
void free_select_list(LC_SELECT_FONT_LIST * sf);

LC_SELECT_FONT_LIST *
insert_select_list(LC_SELECT_FONT_LIST * sf,LC_FONT * f,int size,int pri)
{
LC_SELECT_FONT_LIST * sf2;
	for ( sf2 = sf ; sf2 ; sf2 = sf2->next )
		if ( sf2->font == f && sf2->size == size ) {
			sf2->count ++;
			if ( sf2->pri > pri )
				sf2->pri = pri;
			return sf;
		}
	sf2 = d_alloc(sizeof(*sf2));
	sf2->next = sf;
	sf2->count = 1;
	sf2->pri = pri;
	sf2->font = f;
	sf2->size = size;
	return sf2;
}

void
free_select_list(LC_SELECT_FONT_LIST * sf)
{
LC_SELECT_FONT_LIST * sf2;
	for ( ; sf ; ) {
		sf2 = sf->next;
		d_f_ree(sf);
		sf = sf2;
	}
}

LC_FONT *
__lc_select_font(
	int * ret_size,
	L_CHAR * str,
	int	length,
	LC_WRITING_STYLE * ws,
	int	size,
	LC_WS_COND * cond,
	int flags,

	int * fp_ws_count)
{
L_CHAR * target;
LC_CWS cws;
FONT_PRI * fp;
int i,j;
L_CHAR mask;
LC_SELECT_FONT_LIST * sf, * sf2, * ret;
LC_FONT * ret_f;
FONT_PRI fp_buf[2];
LCF_SET set;
	if ( ws == 0 )
		return 0;
	if ( flags & CBF_SRC_COMBINE )
		target = str;
	else {
		target = combine(str,length);
		length = l_strlen(target);
	}
	cws = _get_cws(ws,cond);
	sf = 0;
	for ( i = 0 ; *str && i < length ; i ++ , str ++ ) {
		mask = get_lc_mask(*str);
		fp = cws_fitting_font_pri(&cws,*str & mask,fp_ws_count);
		if ( fp == 0 )
			continue;
		for ( j = 0 ; fp->font ; fp ++ , j ++ ) {
			fp_buf[0] = *fp;
			fp_buf[1].font = 0;
			if ( _search_ws_font(&set,&fp_buf[0],*str,size) < 0 )
				continue;
			sf = insert_select_list(sf,set.font,set.size,j);
		}
	}
	ret = 0;
	sf2 = sf;
	for ( ; sf2 ; sf2 = sf2->next ) {
		if ( ret == 0 ) {
			ret = sf2;
			continue;
		}
		if ( sf2->count > ret->count ) {
			ret = sf2;
			continue;
		}
		if ( sf2->count == ret->count &&
				sf2->pri < ret->pri ) {
			ret = sf2;
			continue;
		}
	}
	if ( ret == 0 )
		ret_f = 0;
	else {
		ret_f = ret->font;
		*ret_size = ret->size;
	}
	free_select_list(sf);
	_free_lc_cws(cws);
	return ret_f;
}
LC_FONT *
_lc_select_font(
	int * ret_size,
	L_CHAR * str,
	int	length,
	LC_WRITING_STYLE * ws,
	int	size,
	LC_WS_COND * cond,
	int flags)
{
static int fp_ws_init;
int fp_ws_cnt;
LC_FONT * ret;

	fp_ws_cnt = fp_ws_init;
	if ( fp_ws_cnt <= 0 )
		fp_ws_cnt = 1;

	for ( ; ; ) {
		ret = __lc_select_font(ret_size,str,length,ws,size,cond,flags,
				&fp_ws_cnt);
		if ( ret )
			return ret;
		if ( fp_ws_cnt < 0 )
			break;
		fp_ws_cnt ++;
	}
	if ( fp_ws_cnt > 0 )
		fp_ws_init = fp_ws_cnt;
	return 0;
}


LC_FONT *
lc_select_font(
	int * ret_size,
	L_CHAR * str,
	int	length,
	LC_WRITING_STYLE * ws,
	int	size,
	LC_WS_COND * cond,
	int flags)
{
LC_FONT * ret;
	lock_task(wct_lock);
	ret = _lc_select_font(ret_size,str,length,ws,size,cond,flags);
	unlock_task(wct_lock,"lc_Select_font");
	return ret;
}

LCF_SET *
str2lcf(L_CHAR * str,int len,LC_FONT * f,int size)
{
LCF_SET * ret,* p;
int i;
	ret = d_alloc(sizeof(LCF_SET)*len);
	p = ret;
	for ( i = 0 ; i < len ; i ++ ) {
		p->ch = *str;
		p->size = size;
		p->font = f;
		p ++;
		str ++;
	}
	return ret;
}

