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

extern CODE_TABLE_HEADER *	ct_root;

CODE_TABLE_HEADER *
get_code_table_by_lcz(L_CHAR lcz)
{
CODE_TABLE_HEADER * ct;
int i;
CODE_ENTRY * e;
	for ( ct = ct_root ; ct ; ct = ct->next )
		if ( ct->lcz == lcz )
			return ct;
	ct = d_alloc(sizeof(*ct));
	ct->lcz = lcz;
	ct->mask = get_lc_mask(lcz);
	ct->len = - ct->mask;
	ct->tbl = d_alloc(ct->len * sizeof(CODE_ENTRY));
	for ( i = 0 ;  i < ct->len ; i ++ ) {
		e = &ct->tbl[i];
		e->code = ct->lcz|i;
		e->co = e;
	}
	ct->next = ct_root;
	ct_root = ct;
	return ct;
}

CODE_TABLE_HEADER *
get_code_table_by_char(L_CHAR a)
{
CODE_TABLE_HEADER * ct;
	for ( ct = ct_root ; ct ; ct = ct->next )
		if ( (ct->lcz & ct->mask) == (a & ct->mask) )
			return ct;
	return 0;
}

int
col_code_entry(CODE_ENTRY * ce1,CODE_ENTRY * ce2)
{
CODE_ENTRY * _ce1, * _ce2;
L_CHAR m1,m2;
	for ( _ce1 = ce1 ; ; ) {
		for ( _ce2 = ce2 ; ; ) {
			m1 = get_lc_mask(_ce1->code);
			m2 = get_lc_mask(_ce2->code);
			if ( m1 != m2 )
				goto next;
			if ( (m1 & _ce1->code) == (m1 & _ce2->code) )
				return 1;
		next:
			_ce2 = _ce2->co;
			if ( _ce2 == ce2 )
				break;
		}
		_ce1 = _ce1->co;
		if ( _ce1 == ce1 )
			break;
	}
	return 0;
}

void
marge_code_entry(CODE_ENTRY * e1,CODE_ENTRY * e2)
{
CODE_ENTRY * e1_next, * e2_next;
	e1_next = e1->co;
	e2_next = e2->co;
	e1->co = e2_next;
	e2->co = e1_next;
}


L_CHAR colision[2];

int
set_correspond(L_CHAR a,L_CHAR b)
{
CODE_TABLE_HEADER * ct_a,* ct_b;
CODE_ENTRY * a_e,* b_e;

	ct_a = get_code_table_by_char(a);
	ct_b = get_code_table_by_char(b);
	if ( ct_a == 0 || ct_b == 0 )
		er_panic("set_correspond");

	a_e = &ct_a->tbl[(~ct_a->mask) & a];
	b_e = &ct_b->tbl[(~ct_b->mask) & b];
	if ( col_code_entry(a_e,b_e) == 1 ) {
		colision[0] = a;
		colision[1] = b;
		return -1;
	}
	marge_code_entry(a_e,b_e);
	return 0;
}


L_CHAR
code_convert(L_CHAR from,L_CHAR lcz_to)
{
CODE_ENTRY * e, * ep;
CODE_TABLE_HEADER * ct;
L_CHAR mask;
L_CHAR ret;
L_CHAR lcz;

	lcz = lcz_to;
	mask = get_lc_mask(from);
	if ( (mask & from) == (mask & lcz) )
		return from;


	switch ( lcz ) {
	case 0:
	case LCZ_2BC_UNICODE_v3_0_UN:
	case LCZ_2BC_UNICODE_v3_0_JP:
	case LCZ_2BC_UNICODE_v3_0_TW:
	case LCZ_2BC_UNICODE_v3_0_KR:
	case LCZ_2BC_UNICODE_v3_0_CN:
		ct = get_code_table_by_char(LCZ_2BC_UNICODE_v3_0_UN);
		if ( ct ) {
			lcz = LCZ_2BC_UNICODE_v3_0_UN;
			break;
		}
	case LCZ_2BC_UNICODE_v2_0_UN:
	case LCZ_2BC_UNICODE_v2_0_JP:
	case LCZ_2BC_UNICODE_v2_0_TW:
	case LCZ_2BC_UNICODE_v2_0_KR:
	case LCZ_2BC_UNICODE_v2_0_CN:
		ct = get_code_table_by_char(LCZ_2BC_UNICODE_v2_0_UN);
		if ( ct ) {
			lcz = LCZ_2BC_UNICODE_v2_0_UN;
			break;
		}
	case LCZ_2BC_UNICODE_v1_1_UN:
	case LCZ_2BC_UNICODE_v1_1_JP:
	case LCZ_2BC_UNICODE_v1_1_TW:
	case LCZ_2BC_UNICODE_v1_1_KR:
	case LCZ_2BC_UNICODE_v1_1_CN:
		ct = get_code_table_by_char(LCZ_2BC_UNICODE_v1_1_UN);
		if ( ct ) {
			lcz = LCZ_2BC_UNICODE_v1_1_UN;
			break;
		}
		return LCC_ERROR;
	case LCZ_2BC_UNICODE_v3_0_UN_CN:
		ct = get_code_table_by_char(LCZ_2BC_UNICODE_v3_0_UN_CN);
		if ( ct ) {
			lcz = LCZ_2BC_UNICODE_v3_0_UN;
			break;
		}
	case LCZ_2BC_UNICODE_v2_0_UN_CN:
		ct = get_code_table_by_char(LCZ_2BC_UNICODE_v2_0_UN_CN);
		if ( ct ) {
			lcz = LCZ_2BC_UNICODE_v2_0_UN;
			break;
		}
	case LCZ_2BC_UNICODE_v1_1_UN_CN:
		ct = get_code_table_by_char(LCZ_2BC_UNICODE_v1_1_UN_CN);
		if ( ct ) {
			lcz = LCZ_2BC_UNICODE_v1_1_UN;
			break;
		}
		return LCC_ERROR;
	}
	switch ( from & mask ) {
	case 0:
	case LCZ_2BC_UNICODE_v3_0_UN:
	case LCZ_2BC_UNICODE_v3_0_JP:
	case LCZ_2BC_UNICODE_v3_0_TW:
	case LCZ_2BC_UNICODE_v3_0_KR:
	case LCZ_2BC_UNICODE_v3_0_CN:
		ct = get_code_table_by_char(LCZ_2BC_UNICODE_v3_0_UN);
		if ( ct ) {
			from = ((~mask)&from) | LCZ_2BC_UNICODE_v3_0_UN;
			break;
		}
	case LCZ_2BC_UNICODE_v2_0_UN:
	case LCZ_2BC_UNICODE_v2_0_JP:
	case LCZ_2BC_UNICODE_v2_0_TW:
	case LCZ_2BC_UNICODE_v2_0_KR:
	case LCZ_2BC_UNICODE_v2_0_CN:
		ct = get_code_table_by_char(LCZ_2BC_UNICODE_v2_0_UN);
		if ( ct ) {
			from = ((~mask)&from) | LCZ_2BC_UNICODE_v2_0_UN;
			break;
		}
	case LCZ_2BC_UNICODE_v1_1_UN:
	case LCZ_2BC_UNICODE_v1_1_JP:
	case LCZ_2BC_UNICODE_v1_1_TW:
	case LCZ_2BC_UNICODE_v1_1_KR:
	case LCZ_2BC_UNICODE_v1_1_CN:
		ct = get_code_table_by_char(LCZ_2BC_UNICODE_v1_1_UN);
		if ( ct ) {
			from = ((~mask)&from) | LCZ_2BC_UNICODE_v1_1_UN;
			break;
		}
		return LCC_ERROR;
	case LCZ_2BC_UNICODE_v3_0_UN_CN:
		ct = get_code_table_by_char(LCZ_2BC_UNICODE_v3_0_UN_CN);
		if ( ct ) {
			from = ((~mask)&from) | LCZ_2BC_UNICODE_v3_0_UN_CN;
			break;
		}
	case LCZ_2BC_UNICODE_v2_0_UN_CN:
		ct = get_code_table_by_char(LCZ_2BC_UNICODE_v2_0_UN_CN);
		if ( ct ) {
			from = ((~mask)&from) | LCZ_2BC_UNICODE_v2_0_UN_CN;
			break;
		}
	case LCZ_2BC_UNICODE_v1_1_UN_CN:
		ct = get_code_table_by_char(LCZ_2BC_UNICODE_v1_1_UN_CN);
		if ( ct ) {
			from = ((~mask)&from) | LCZ_2BC_UNICODE_v1_1_UN_CN;
			break;
		}
		return LCC_ERROR;
	default:
		ct = get_code_table_by_char(from);
		if ( ct == 0 )
			return LCC_ERROR;
	}


	e = &ct->tbl[(~ct->mask) & from];
	mask = get_lc_mask(lcz);

	for ( ep = e ; ; ) {

		if ( (ep->code & mask) == (lcz & mask) ) {
			ret = ep->code;
			goto ok;
		}
		ep = ep->co;
		if ( ep == e )
			break;
	}
	return LCC_ERROR;
ok:
	if ( lcz != lcz_to ) {
		mask = get_lc_mask(lcz);
		ret = (ret & (~mask)) | lcz_to;
	}
	return ret;
}


L_CHAR
code_convert_set(L_CHAR from,LCZ_SET * set1,LCZ_SET * set2)
{
L_CHAR ret;
L_CHAR mask;
	if ( set1 == 0 && set2 == 0 )
		return from;
	if ( set1 )
		for ( ; set1->lcz != LCC_ERROR ; set1 ++ ) {
			ret = code_convert(from,set1->lcz);
			mask = set1->mask | get_lc_mask(set1->lcz);
			if ( ret != LCC_ERROR && 
				(ret&mask) == (set1->lcz&mask) )
				return ret;
		}
	if ( set2 )
		for ( ; set2->lcz != LCC_ERROR ; set2 ++ ) {
			ret = code_convert(from,set2->lcz);
			mask = set2->mask | get_lc_mask(set2->lcz);
			if ( ret != LCC_ERROR &&
				(ret&mask) == (set2->lcz&mask) )
				return ret;
		}
	return LCC_ERROR;
}


int
cmp_l_code(L_CHAR a,L_CHAR b)
{
CODE_TABLE_HEADER * ct_a,* ct_b;
CODE_ENTRY * a_e,* b_e, * e;

	ct_a = get_code_table_by_char(a);
	ct_b = get_code_table_by_char(b);
	if ( ct_a == 0 || ct_b == 0 )
		return -1;

	a_e = &ct_a->tbl[(~ct_a->mask) & a];
	b_e = &ct_b->tbl[(~ct_b->mask) & b];
	for ( e = a_e ; ; ) {
		if ( e == b_e )
			return 0;
		e = e->co;
		if ( e == a_e )
			return -1;
	}
}


L_CHAR *
get_all_l_char(int *sizep,L_CHAR a)
{
int size,i;
CODE_TABLE_HEADER * ct;
CODE_ENTRY * e, * ep;
L_CHAR * ret;
	ct = get_code_table_by_char(a);
	if ( ct == 0 ) {
		*sizep = 0;
		return 0;
	}
	ep = &ct->tbl[(~ct->mask) & a];
	size = 0;
	for ( e = ep ; ; ) {
		size ++;
		e = e->co;
		if ( e == ep )
			break;
	}
	*sizep = size;
	ret = d_alloc(sizeof(L_CHAR)*size);
	i = 0;
	for ( e = ep ; ; ) {
		ret[i] = e->code;
		e = e->co;
		if ( e == ep )
			break;
		i ++;
	}
	return ret;
}


