/**********************************************************************
 
	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	<stdio.h>
#include	"long_char.h"
#include	"lcconv.h"
#include	"avt.h"
#include	"memory_debug.h"
#include	"utils.h"

typedef struct cn_list {
	struct cn_list *	next;
	int			len;
	LC_CN			cn;
} CN_LIST;

AVT_NODE *	comb_tree;
AVT_NODE *	node_tree;
AVT_NODE *	cn_tree_cn;
AVT_NODE *	cn_tree_lcz;
CN_LIST *	cn_list_head;

int
comb_cmp(LC_COMBINATION * n1,LC_COMBINATION * n2)
{
	return l_strcmp(n1->leaf,n2->leaf);
}

char *
seek_buf(char * p)
{
	for ( ; *p && 
		*p != ' ' && 
		*p != '\t' && 
		*p != '\n' &&
		*p != '\r' ;
		p ++);
	for ( ; *p &&
		(*p == ' ' ||
		*p == '\t' ||
		*p == '\n' ||
		*p == '\r');
		p ++ );
	return p;
}

void
load_combination(char * filename)
{
FILE * f;
char * buf;
char * cmd;
CN_LIST * cnl;
char * p;
AVT_NODE * n;
LC_COMBINATION * lcc;
int leaf_len;
	f = fopen(filename,"r");
	if ( f == 0 )
		return;
	buf = d_alloc(1002);
	cmd = d_alloc(100);
	for ( ; ; ) {
		if ( fgets(buf,1000,f) == 0 )
			break;
		cmd[0] = 0;
		sscanf(buf,"%s",cmd);
		if ( cmd[0] == 0 )
			continue;
		if ( strcmp(cmd,"cn") == 0 ) {
			cnl = d_alloc(sizeof(*cnl));
			sscanf(buf,"%s %x %x %i\n",
				cmd,
				(int*)&cnl->cn.lcz,
				(int*)&cnl->cn.cn,
				(int*)&cnl->len);
			cnl->cn.max = 0;
			cnl->next  = cn_list_head;
			cn_list_head = cnl;
		}
		else if ( strcmp(cmd,"node") == 0 ) {
			lcc = d_alloc(sizeof(*lcc));
			lcc->leaf = d_alloc(sizeof(L_CHAR));
			leaf_len = 0;
			p = seek_buf(buf);
			sscanf(p,"%x",(int*)&lcc->node);
			p = seek_buf(p);
			for ( ; *p && *p != '\n' && *p != '\r' ; ) {
				lcc->leaf = d_re_alloc(lcc->leaf,
					sizeof(L_CHAR)*(leaf_len+2));
				sscanf(p,"%x",(int*)&lcc->leaf[leaf_len]);
				leaf_len ++;
				p = seek_buf(p);
			}
			lcc->leaf[leaf_len] = 0;
			n = d_alloc(sizeof(*n));
			n->data = lcc;
			avt_insert(&comb_tree,n,comb_cmp);
		}
		else {
			fprintf(stderr,"unsupport %s\n",cmd);
			exit(1);
		}
	}
	fclose(f);
	d_f_ree(cmd);
	d_f_ree(buf);
}

int
save_trace(AVT_NODE * a,FILE * f)
{
LC_COMBINATION * c;
L_CHAR * ptr;
int max;
CN_LIST * cnl;
L_CHAR mask;
	c = a->data;
	fprintf(f,"node\t0x%x",
		(int)c->node);
	max = 0;
	for ( ptr = c->leaf ; *ptr ; ptr ++ ) {
		fprintf(f,"\t0x%x",(int)*ptr);
		max ++;
	}
	fprintf(f,"\n");

	mask = get_lc_mask(c->leaf[0]);
	for ( cnl = cn_list_head ; cnl ; cnl = cnl->next )
		if ( (cnl->cn.lcz & mask) == (c->leaf[0] & mask) ) {
			if ( cnl->cn.max < max )
				cnl->cn.max = max;
			return 0;
		}
	fprintf(stderr,"SYSTEM ERROR\n");
	exit(1);
	return 0;
}

void
save_combination(char * filename)
{
FILE * f;
CN_LIST * cnl;
	f = fopen(filename,"w+");
	if ( f == 0 ) {
		fprintf(stderr,"save error\n");
		exit(1);
	}
	avt_trace_from_small(comb_tree,save_trace,f);
	for ( cnl = cn_list_head ; cnl ; cnl = cnl->next ) {
		fprintf(f,"cn\t0x%x\t0x%x\t%i\t%i\n",
			(int)cnl->cn.lcz,
			(int)cnl->cn.cn,
			cnl->len,
			(int)cnl->cn.max);
	}
}

int
save_leaf_1(AVT_NODE * a,FILE * f)
{
LC_COMBINATION * c;
int i;
	c = a->data;
	fprintf(f,"L_CHAR leaf_%x[] = {\n",(int)c->node);
	for ( i = 0 ; c->leaf[i] ; i ++ )
		fprintf(f,"\t0x%x,\n",(int)c->leaf[i]);
	fprintf(f,"\t0};\n\n");
	return 0;
}

void
save_leaf(FILE * f)
{
	avt_trace_from_small(comb_tree,save_leaf_1,f);
}

typedef struct sln_t {
	FILE *	f;
	int	cnt;
	int	flag;
} SLN_T;

int
save_combination_1(AVT_NODE * a,SLN_T * w)
{
LC_COMBINATION * c;
	if ( w->flag )
		fprintf(w->f,",\n");
	c = a->data;
	fprintf(w->f,"\t{0x%x,&leaf_%x[0]}",
		(int)c->node,(int)c->node);
	w->flag = 1;
	w->cnt ++;
	return 0;
}


void
save_leaf_to_node(FILE * f)
{
SLN_T w;
	w.f = f;
	w.cnt = 0;
	w.flag = 0;
	fprintf(f,"LC_COMBINATION leaf_to_node[] = {\n");
	avt_trace_from_small(comb_tree,save_combination_1,&w);
	fprintf(f,"};\n\n");
}

int
node_cmp(LC_COMBINATION * c1,LC_COMBINATION * c2)
{
	if ( ((int)c1->node) < ((int)c2->node) )
		return -1;
	if ( ((int)c1->node) > ((int)c2->node) )
		return 1;
	return 0;
}

int
sort_node_1(AVT_NODE * a)
{
AVT_NODE * n;
	n = d_alloc(sizeof(*n));
	n->data = a->data;
	avt_insert(&node_tree,n,node_cmp);
	return 0;
}

void
sort_node()
{
	avt_trace_from_small(comb_tree,sort_node_1,0);
}


void
save_node_to_leaf(FILE * f)
{
SLN_T w;
	w.f = f;
	w.flag = 0;
	w.cnt = 0;

	fprintf(f,"LC_COMBINATION node_to_leaf[] = {\n");
	avt_trace_from_small(node_tree,save_combination_1,&w);
	fprintf(f,"};\n\n");
	fprintf(f,"int node_count = %i;\n\n",w.cnt);
}

int
cn_lcz_cmp(CN_LIST * c1,CN_LIST * c2)
{
	if ( c1->cn.lcz > c2->cn.lcz )
		return 1;
	if ( c1->cn.lcz < c2->cn.lcz )
		return -1;
	return 0;
}

int
cn_cn_cmp(CN_LIST * c1,CN_LIST * c2)
{
	if ( c1->cn.cn > c2->cn.cn )
		return 1;
	if ( c1->cn.cn < c2->cn.cn )
		return -1;
	return 0;
}

int cn_len;

void
sort_cn()
{
CN_LIST * cnl;
AVT_NODE * a;
	cn_len = 0;
	for ( cnl = cn_list_head ; cnl ; cnl = cnl->next ) {
		a = d_alloc(sizeof(*a));
		a->data = cnl;
		avt_insert(&cn_tree_lcz,a,cn_lcz_cmp);
		a = d_alloc(sizeof(*a));
		a->data = cnl;
		avt_insert(&cn_tree_cn,a,cn_cn_cmp);
		cn_len ++;
	}
}

int
save_cn_1(AVT_NODE * a,SLN_T *w)
{
CN_LIST * cnl;
	if ( w->flag )
		fprintf(w->f,",\n");
	cnl = a->data;
	fprintf(w->f,"\t{0x%x,0x%x,%i}",
		(int)cnl->cn.lcz,
		(int)cnl->cn.cn,
		(int)cnl->cn.max);
	w->flag = 1;
	return 0;
}

void
save_cn_table(FILE * f)
{
SLN_T w;
	w.f = f;
	w.cnt = 0;
	w.flag = 0;
	fprintf(f,"LC_CN cn_lcz_sort[] = {\n");
	avt_trace_from_small(cn_tree_lcz,save_cn_1,&w);
	fprintf(f,"};\n\n");

	w.f = f;
	w.cnt = 0;
	w.flag = 0;
	fprintf(f,"LC_CN cn_cn_sort[] = {\n");
	avt_trace_from_small(cn_tree_cn,save_cn_1,&w);
	fprintf(f,"};\n\n");
	fprintf(f,"int cn_len = %i;\n\n",cn_len);
}

void
save_ccode(char * filename)
{
FILE * f;
	f = fopen(filename,"w+");
	if ( f == 0 ) {
		fprintf(stderr,"save ccode error\n");
		exit(1);
	}
	fprintf(f,"\n");
	fprintf(f,"#include\t\"long_char.h\"\n\n");
	save_leaf(f);
	save_leaf_to_node(f);
	sort_node();
	save_node_to_leaf(f);
	sort_cn();
	save_cn_table(f);
	fclose(f);
}

void
new_combination(L_CHAR lcz,L_CHAR cn)
{
CN_LIST * cnl;
	for ( cnl = cn_list_head ; cnl ; cnl = cnl->next )
		if ( cnl->cn.lcz == lcz )
			return;
	cnl = d_alloc(sizeof(*cnl));
	cnl->cn.lcz = lcz;
	cnl->cn.cn = cn;
	cnl->cn.max = cnl->len = 0;
	cnl->next = cn_list_head;
	cn_list_head = cnl;
}

L_CHAR
get_node(L_CHAR * set)
{
LC_COMBINATION * cb;
AVT_NODE * n, * ret;
L_CHAR mask;
L_CHAR node;
CN_LIST * cnl;
	mask = get_lc_mask(set[0]);
	for ( cnl = cn_list_head ; cnl ; cnl = cnl->next )
		if ( (cnl->cn.lcz & mask) == (set[0] & mask) )
			goto ok;
	fprintf(stderr,"invalid node %x %x\n",(int)set[0],(int)set);
	exit(1);
ok:
	cb = d_alloc(sizeof(*cb));
	cb->node = 0;
	cb->leaf = ll_copy_str(set);

	n = d_alloc(sizeof(*n));
	n->data = cb;
	ret = avt_insert(&comb_tree,n,comb_cmp);
	if ( ret == n ) {
		node = cnl->cn.cn | cnl->len;
		cnl->len ++;
		cb->node = node;
		return node;
	}
	else {
		d_f_ree(cb->leaf);
		d_f_ree(cb);
		cb = ret->data;
		return cb->node;
	}
}
