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

#define F_LR_RL 	0x0001

#define BSIZE 1000
#define MAXCOL	10

FILE * invalid_fd;

void (*conv_func)();
char buffer[BSIZE];
char col[MAXCOL][100];

L_CHAR colision[2];

CODE_TABLE_HEADER * ct_unicode;
CODE_TABLE_HEADER * ct_rk;
CODE_TABLE_HEADER * ct_jisx0;
CODE_TABLE_HEADER * ct_jisx1;
CODE_TABLE_HEADER * ct_jisx2;
CODE_TABLE_HEADER * ct_jisx_s;

CODE_TABLE_HEADER * ct_root;

L_CHAR
output_jisx(char * str,L_CHAR mask)
{
int a;
int ku,ten,code;

	a = atoi(&str[2]);
	ku = a/100;
	ten = a%100;
	code = ((ku+0x20)<<8) + (ten+0x20);
	return mask|code;
}


int
get_col_row(char * str)
{
char * s1, * s2;
	s1 = &str[2];
	for ( s2 = s1 ; *s2 && *s2 != '/' ; s2 ++ );
	if ( *s2 == 0 ) {
		fprintf(stderr,"get_col_row error %s\n",str);
		exit(1);
	}
	*s2 = 0;
	s2 ++;
	return (atoi(s1)<<4) +  atoi(s2);
}

L_CHAR
output_jisx0201_r(char * col)
{
int code;
	code = get_col_row(col);
	return LCZ_1BC_JISX0201|code;
}

L_CHAR
output_jisx0201_k(char * col)
{
int code;
	code = get_col_row(col);
	return LCZ_1BC_JISX0201|0x80|code;
}


void
invalid_unicode(char * filename,char * line)
{
	if ( invalid_fd == 0 )
		return;
	fprintf(invalid_fd,"%s\t%s",filename,line);
}

int
yasuoka_a(FILE * fd)
{
char * s;
int i;
L_CHAR unicode,rk_code,jisx0_code,jisx1_code,jisx2_code,jisx_s_code;
int c_ret;

	ct_unicode = get_code_table_by_lcz(LCZ_2BC_UNICODE_v1_1_UN);
	ct_rk = get_code_table_by_lcz(LCZ_1BC_JISX0201);
	ct_jisx0 = get_code_table_by_lcz(LCZ_2BC_JISX0208_1978);
	ct_jisx1 = get_code_table_by_lcz(LCZ_2BC_JISX0208_1983);
	ct_jisx2 = get_code_table_by_lcz(LCZ_2BC_JISX0208_1990);
	ct_jisx_s = get_code_table_by_lcz(LCZ_2BC_JISX0212_1990);

	for ( ; ; ) {
		s = fgets(buffer,BSIZE-1,fd);
		if ( s == 0 )
			break;
		sscanf(buffer,"%s %s %s %s %s %s\n",
			col[0],
			col[1],
			col[2],
			col[3],
			col[4],
			col[5]);
		if ( col[0][0] == '#' )
			continue;
		sscanf(col[0],"%X",(int*)&unicode);
		unicode |= LCZ_2BC_UNICODE_v1_1_UN;

		rk_code = jisx0_code = jisx1_code = jisx2_code
			= jisx_s_code = 0;

		for ( i = 1 ; i < MAXCOL ; i ++ ) {
			c_ret = 0;
			switch ( col[i][0] ) {
			case '#':
				goto next;
			case 'R':
				rk_code = output_jisx0201_r(col[i]);
				if ( rk_code )
					c_ret = set_correspond(
						unicode,
						rk_code);
				break;
			case 'K':
				rk_code = output_jisx0201_k(col[i]);
				if ( rk_code )
					c_ret = set_correspond(
						unicode,
						rk_code);
				break;
			case '0':
				jisx0_code = output_jisx(col[i],
					LCZ_2BC_JISX0208_1978);
				if ( jisx0_code )
					c_ret = set_correspond(
						unicode,
						jisx0_code);
				break;
			case '1':
				jisx1_code = output_jisx(col[i],
					LCZ_2BC_JISX0208_1983);
				if ( jisx1_code )
					c_ret = set_correspond(
						unicode,
						jisx1_code);
				break;
			case '2':
				jisx2_code = output_jisx(col[i],
					LCZ_2BC_JISX0208_1990);
				if ( jisx2_code )
					c_ret = set_correspond(
						unicode,
						jisx2_code);
				break;
			case 'S':
				jisx_s_code = output_jisx(col[i],
					LCZ_2BC_JISX0212_1990);
				if ( jisx_s_code )
					c_ret = set_correspond(
						unicode,
						jisx_s_code);
				break;
			default:
				fprintf(stderr,"???? %s\n",col[i]);
				exit(1);
			}
			if ( c_ret < 0 ) {
				fprintf(stderr,"COLISION %x - %x\n",
					(int)colision[0],(int)colision[1]);
			}
		}
	next:
		{}
	}
	return 0;
}


void
setup_code(L_CHAR * code,L_CHAR field_code[4])
{
int byte;
	if ( ((*code) & 0xffffff00) == 0 )
		byte = 1;
	else if ( ((*code) & 0xffff0000) == 0 )
		byte = 2;
	else if ( ((*code) & 0xff000000) == 0 )
		byte = 3;
	else	return;
	for ( ; byte < 4 ; byte ++ )
		if ( field_code[byte] )
			break;
	if ( byte == 4 ) {
		fprintf(stderr,"field code error\n");
		exit(1);
	}
	*code = *code | field_code[byte];
}

int
replace_plus(char * str,L_CHAR field,L_CHAR comb)
{
L_CHAR * set;
char * p;
int len;
int i;
L_CHAR node;
char * seek_buf();
L_CHAR get_node(L_CHAR*);
	len = 1;
	for ( p = str ; *p ; p ++ )
		if ( *p == '+' ) {
			*p = ' ';
			len ++;
		}
	set = d_alloc(sizeof(L_CHAR)*(len+1));
	i = 0;
	for ( p = str ; *p ; p = seek_buf(p) ) {
		sscanf(p,"%x",(int*)&set[i]);
		set[i] |= field;
		i ++;
	}
	set[i] = 0;
	node = get_node(set);
	d_f_ree(set);
	sprintf(str,"+0x%x",(int)node);
	return 0;
}

int
check_byte(char * str,L_CHAR * field)
{
char * p;
char ch;
L_CHAR code;
int byte;
	for ( p = str ; *p && *p != '+' ; p ++ );
	ch = *p;
	*p = 0;
	sscanf(str,"%x",(int*)&code);
	*p = ch;

	if ( (code & 0xffffff00) == 0 )
		byte = 1;
	else if ( (code & 0xffff0000) == 0 )
		byte = 2;
	else if ( (code & 0xff000000) == 0 )
		byte = 3;
	for ( ; byte < 4 && field[byte] == 0 ; byte ++);
	if ( byte == 4 ) {
		fprintf(stderr,"check_byte");
		exit(1);
	}
	return byte;
}

int
combination_check(char * filename,char * buffer,
	int lr,
	L_CHAR field[MAXCOL][2][4],
	L_CHAR cc[MAXCOL][2][4])
{
int i,j;
L_CHAR f,c;
int byte;
	for ( i = 0 ; i < MAXCOL ; i ++ ) {
		if ( col[i][0] == '#' )
			break;
		for ( j = 0 ; col[i][j] ; j ++ )
			if ( col[i][j] == '+' ) {
				byte = check_byte(col[i],field[i][lr]);
				f = field[i][lr][byte];
				c = cc[i][lr][byte];
				if ( c == 0 ) {
					invalid_unicode(filename,buffer);
					return -1;
				}
				replace_plus(col[i],f,c);
			}
	}
	return 0;
}

int
f_lr_rl(char * str)
{
int ret;
	if ( memcmp(str,"<LR>+",5) == 0 ) {
		ret = 0;
		goto hit;
	}
	if ( memcmp(str,"<RL>+",5) == 0 ) {
		ret = 1;
		goto hit;
	}
	return 0;
hit:
	memmove(str,&str[5],strlen(&str[5])+1);
	return ret;
}

int
tab(FILE * fd,char * filename)
{
int i,j,k;
L_CHAR code1, code2;
CODE_TABLE_HEADER * ct;
char * s;
L_CHAR field_code[MAXCOL][2][4];
L_CHAR combination_code[MAXCOL][2][4];
int flags_code[MAXCOL];
int target_field,byte;
L_CHAR _code;
int lr;
	for ( i = 0 ; i < MAXCOL ; i ++ ) {
		for ( j = 0 ; j < 2 ; j ++ ) {
			for ( k = 0 ; k < 4 ; k ++ ) {
				field_code[i][j][k] = 0;
				combination_code[i][j][k] = 0;
			}
		}
		flags_code[i] = 0;
	}
	target_field = -1;
	for ( ; ; ) {
		s = fgets(buffer,BSIZE-1,fd);
		if ( s == 0 )
			break;
		for  ( i = 0 ; i < MAXCOL ; i ++ )
			col[i][0] = 0;
		sscanf(buffer,"%s %s %s %s %s %s\n",
			col[0],
			col[1],
			col[2],
			col[3],
			col[4],
			col[5]);
		if ( col[0][0] == 0 )
			continue;
		if ( col[0][0] == '#' )
			continue;
		if ( strcmp(col[0],"field") == 0 ) {
			target_field = atoi(col[1]);
			continue;
		}
		else if ( strcmp(col[0],"code") == 0 ) {
			if ( target_field == -1 ) {
				fprintf(stderr,"target field error\n");
				exit(1);
			}
			byte = atoi(col[1]);
			sscanf(col[2],"%x",(int*)&_code);
			if ( strcmp(col[3],"") == 0 ) {
				field_code[target_field][0][byte] = _code;
				field_code[target_field][1][byte] = _code;
			}
			else if ( strcmp(col[3],"LR") == 0 ) {
				field_code[target_field][0][byte] = _code;
			}
			else if ( strcmp(col[3],"RL") == 0 ) {
				field_code[target_field][1][byte] = _code;
			}
			else {
				fprintf(stderr,"%s code command error\n",
					filename);
			}
			continue;
		}
		else if ( strcmp(col[0],"combination") == 0 ) {
			if ( target_field == -1 ) {
				fprintf(stderr,"target field error\n");
				exit(1);
			}
			byte = atoi(col[1]);
			sscanf(col[2],"%x",(int*)&_code);
			if ( strcmp(col[3],"") == 0 ) {
				combination_code
					[target_field][0][byte] = _code;
				combination_code
					[target_field][1][byte] = _code;
				new_combination(
					field_code[target_field][0][byte],
					_code);
				new_combination(
					field_code[target_field][1][byte],
					_code);
			}
			else if ( strcmp(col[3],"LR") == 0 ) {
				combination_code
					[target_field][0][byte] = _code;
				new_combination(
					field_code[target_field][0][byte],
					_code);
			}
			else if ( strcmp(col[3],"RL") == 0 ) {
				combination_code
					[target_field][1][byte] = _code;
				new_combination(
					field_code[target_field][1][byte],
					_code);
			}
			else {
				fprintf(stderr,"%s combination command error\n",
					filename);
			}
			continue;
		}
		else if ( strcmp(col[0],"bi") == 0 ) {
			flags_code[atoi(col[1])] |= F_LR_RL;
			continue;
		}
		lr = 0;
		for ( i = 0 ; i < MAXCOL ; i ++ ) {
			if ( flags_code[i] & F_LR_RL )
				lr = f_lr_rl(col[i]);
		}
		if ( combination_check(filename,buffer,
				lr,
				field_code,
				combination_code) < 0 )
			continue;
		sscanf(col[0],"%x",(int*)&code1);
		if ( col[0][0] != '+' )
			setup_code(&code1,field_code[0][lr]);
		ct = get_code_table_by_lcz(code1&(get_lc_mask(code1)));
		for ( i = 1 ; i < MAXCOL ; i ++ ) {
			if ( col[i][0] == '#' )
				break;
			sscanf(col[i],"%x",(int*)&code2);
			if ( col[i][0] != '+' )
				setup_code(&code2,field_code[i][lr]);
			ct = get_code_table_by_lcz(
				code2&get_lc_mask(code2));
			if ( set_correspond(code1,code2) < 0 )
				fprintf(stderr,"COLISION %x - %x\n",
					(int)colision[0],
					(int)colision[1]);
		}
	}
	return 0;
}

int
convert(int (*func)(),int * argp,char ** argv)
{
FILE * fd;
int ret;
	if ( strcmp(argv[(*argp)+1],"-")== 0 )
		fd = stdin;
	else {
		fprintf(stderr,">> %s\n",argv[(*argp)+1]);
		fd = fopen(argv[(*argp)+1],"r");
		if ( fd == 0 ) {
			fprintf(stderr,"cannot open the file %s\n",
				argv[(*argp)+1]);
			exit(1);
		}
	}
	ret = (*func)(fd,argv[(*argp)+1]);
	*argp = (*argp) + 2;
	return ret;
}


int
main(int argc,char ** argv)
{
int argp;
char * comb_file = 0;
	if ( argc < 2 ) {
		fprintf(stderr,"too few argments\n");
		exit(1);
	}
	argp = 1;
	for ( ; argp < argc ; ) {
		if ( strcmp(argv[argp],"tab") == 0 ) {
			if ( convert(tab,&argp,argv) < 0 )
				break;
		}
		else if ( strcmp(argv[argp],"comb") == 0 ) {
			load_combination(argv[argp+1]);
			comb_file = argv[argp+1];
			argp += 2;
		}
		else if ( strcmp(argv[argp],"yasuoka-a") == 0 ) {
			if ( convert(yasuoka_a,&argp,argv) < 0 )
				break;
		}
		else if ( strcmp(argv[argp],"c") == 0 ) {
			print_code_table_c();
			argp ++;
		}
		else if ( strcmp(argv[argp],"t") == 0 ) {
			print_code_table_t();
			argp ++;
		}
		else if ( strcmp(argv[argp],"inv") == 0 ) {
			invalid_fd = fopen(argv[argp+1],"w+");
			argp += 2;
		}
		else if ( strcmp(argv[argp],"comb-code") == 0 ) {
			if ( comb_file == 0 ) {
				fprintf(stderr,"combination db is required\n");
				exit(1);
			}
	  		save_combination(comb_file);
			save_ccode(argv[argp+1]);
			argp += 2;
		}
		else {
			fprintf(stderr,"invalid format (%s)\n",argv[argp]);
			exit(1);
		}
	}

	fprintf(stderr,"ALL FINISHED\n");
	return 0;
}


