/**********************************************************************
 
	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	"memory_debug.h"
#include	"utils.h"
#include	"long_char.h"
#include	"lc_encode.h"
#include	"text_render.h"
#include	"tr_lang.h"


#define BITSOF_L_CHAR	(sizeof(L_CHAR)*8)
#define BITSOF_MASK	8
#define L_MASK		((1<<BITSOF_MASK)-1)
#define LEVELS		(BITSOF_L_CHAR/BITSOF_MASK)
#define ENTRY_NOS	(1<<BITSOF_MASK)

typedef struct trl_entry {
	struct trl_entry *	down;
	TRL_LANG_LIST *		langs;
} TRL_ENTRY;


TRL_ENTRY * _trl_new_entry(TRL_LANG_LIST * ll);
void _trl_insert_table(L_CHAR from,L_CHAR to,TR_LANG * lang,
	TRL_ENTRY * e,int shift);

TRL_ENTRY *		trl_hash;


TRL_LANG_LIST *
_trl_copy_langs(TRL_LANG_LIST * ll)
{
	if ( ll == 0 )
		return 0;
	ll->ref ++;
	return ll;
}

TRL_LANG_LIST *
_trl_new_langs(TRL_LANG_LIST * ll,TR_LANG * lang)
{
TRL_LANG_LIST * ret;
	for ( ret = ll ; ret ; ret = ret->next )
		if ( ret->lang == lang )
			return ll;
	ret = d_alloc(sizeof(*ret));
	memset(ret,0,sizeof(*ret));
	ret->ref = 1;
	ret->lang = lang;
	ret->next = ll;
	return ret;
}

void
_trl_free_langs(TRL_LANG_LIST * ll)
{
TRL_LANG_LIST * ll_2;
	for ( ; ll ; ) {
		ll->ref --;
		if ( ll->ref )
			return;
		ll_2 = ll->next;
		d_f_ree(ll);
		ll = ll_2;
	}
}

TRL_ENTRY *
_trl_new_entry(TRL_LANG_LIST * ll)
{
TRL_ENTRY * ret;
int i;
	ret = d_alloc(sizeof(TRL_ENTRY)*ENTRY_NOS);
	memset(ret,0,sizeof(TRL_ENTRY)*ENTRY_NOS);
	for ( i = 0 ; i < ENTRY_NOS ; i ++ )
		ret[i].langs = _trl_copy_langs(ll);
	return ret;
}

void
_trl_insert_table(L_CHAR from,L_CHAR to,TR_LANG * lang,
	TRL_ENTRY * e,int shift)
{
L_CHAR p;
unsigned long _p,_stop,_next;
TRL_ENTRY * target;
	for ( p = from ; ((unsigned long)p) < ((unsigned long)to) ; ) {
		_p = p & (-(1<<shift));
		_stop = _p + (1<<shift);
		if ( _stop > to )
			_next = to;
		else	_next = _stop;
		target = &e[(_p>>shift)&L_MASK];
		if ( p == _p && _next == _stop ) {
			if ( target->down == 0 ) {
				target->langs = _trl_new_langs(
						target->langs,lang);
			}
			else {
				_trl_insert_table(
					_p,_next,
					lang,target->down,
					shift - BITSOF_MASK);
			}
		}
		else {
			if ( target->down == 0 ) {
				target->down = _trl_new_entry(target->langs);
				_trl_free_langs(target->langs);
			}
			_trl_insert_table(
				p,_next,
				lang,target->down,
				shift - BITSOF_MASK);
		}
		p = _next;
	}
}

void
trl_insert_table(L_CHAR from ,L_CHAR to,TR_LANG * lang,int convert_flag)
{
L_CHAR p;
L_CHAR * p_list;
int size;
int i;

	if ( trl_hash == 0 )
		trl_hash = _trl_new_entry(0);
	_trl_insert_table(from,to,lang,trl_hash,BITSOF_L_CHAR - BITSOF_MASK);
	if ( convert_flag == 0 )
		return;
	for ( p = from ; ((unsigned long)p) < ((unsigned long)to) ; p ++ ) {
		p_list = get_all_l_char(&size,p);
		if ( size == 0 )
			continue;
		for ( i = 0 ; i < size ; i ++ ) {
			_trl_insert_table(p_list[i],p_list[i]+1,
				lang,
				trl_hash,
				BITSOF_L_CHAR-BITSOF_MASK);
		}
		d_f_ree(p_list);
	}
}


TR_LANG *
trl_get_lang(TRL_CONTEXT * ctx,L_CHAR ch)
{
TRL_ENTRY * e;
int shift;
TRL_LANG_LIST * ll, ** cllp, * llp, * cll;
	shift = BITSOF_L_CHAR - BITSOF_MASK;
	if ( trl_hash == 0 )
		return 0;
	ll = 0;
	for ( e = trl_hash ; ; ) {
		e = &e[(ch>>shift)&L_MASK];
		if ( e->down == 0 ) {
			ll = e->langs;
			break;
		}
		e = e->down;
		shift -= BITSOF_MASK;
	}
	if ( ll == 0 )
		return 0;
	if ( ctx == 0 )
		return ll->lang;
	for ( cllp = &ctx->used ; *cllp ; cllp = &(*cllp)->next ) {
		for ( llp = ll ; llp ; llp = llp->next )
			if ( llp->lang == (*cllp)->lang ) {
				cll = *cllp;
				*cllp = cll->next;
				cll->next = ctx->used;
				ctx->used = cll;
				return cll->lang;
			}
	}
	ctx->used = _trl_new_langs(ctx->used,ll->lang);
	return ll->lang;
}



