//
//  UNICON - The Console Chinese & I18N
//  Copyright (c) 1999-2002
//
//  This file is part of UNICON, a console Chinese & I18N
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  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.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//  See the file COPYING directory of this archive
//  Author: see CREDITS

#include <stdio.h>
#include <stdarg.h> 
#include <ctype.h> 
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include "xl_hzinput.h"
#include "xl_sysphrase.h"
#include "xl_phrase.h"

//
// Documentation for cin2tab.
//
// cin2tab is a program responsible for generating the system phrase file and 
// input code translation file. The author of this document (me) and the author
// of this chinese input method module is two different person. I'd would like
// to make some definition for some keywords, in order to reduce confusion.
//
// Multi-byte: For asian, most characters cannot be represent by 8 bit. Hence
//             more than one byte is used to represent character.
//             Multi-byte character is sometimes abbreviated as MB
//             Multi-byte string is sometimes abbreviated as MBS
// Input Code: A sequence of ASCII character, which should be translated into
//             one multi-byte character, or multi-byte string.
//             In some documentation, 'Input Code' is also expressed as 'Input Stroke',
//             and 'Input Key'
// Phrase:     This is a common word in Chinese Input envoirnment. For most
//             Chinese input system, a phrase can be a single Chinese character
//             or a Chinese character string.

// maximum char/phrase can be defined
#define MAXSIZE            65535
static ITEM				ItemOut[MAXSIZE];
static AssociatePhrase	MyPhraseItem[MAXSIZE];
static TL_SysPhrase_T	*pDefaultSysphrase = NULL;

static FILE				*fr;
static int				lineno;

void print_error (char *fmt,...)
{
	va_list args;

	va_start (args, fmt);
	vfprintf (stderr, fmt, args);
	va_end (args);
	fprintf (stderr,"\n");
	exit (-1);
}

// Return the 1st char which isn't white space
char *skip_space (char *s)
{
	while (*s && (*s == ' ' || *s == '\t'))
		s++;

	return s;
}

char *to_space (char *s)
{
	while (*s && *s!=' ' && *s!='\t')
		s++;

	return s;
}

void del_nl_space (char *s)
{
	char *t;

	int len = strlen (s);
	if (!*s) 
		return;
	t = s + len - 1;
	while ((*t=='\n' || *t==' ' || *t=='\t') && s<t) 
		t--;
	*(t+1)=0;
}

void get_line (u_char *tt)
{
	while (! feof(fr)) 
	{
		fgets (tt, 128, fr);
		lineno++;
		if (tt[0] == '#') 
			continue;
		else  
			break;
	}
}

void cmd_arg (u_char *s, u_char **cmd, u_char **arg)
{
	char *t;

	get_line(s);
	if (!*s)
	{
		*cmd = *arg = s;
		return;
	}

	s = skip_space(s);
	t = to_space(s);
	*cmd = s;
	if (!(*t)) {
		*arg = t;
		return;
	}
	*t=0;
	t++;
	t=skip_space(t);
	del_nl_space(t);
	*arg=t;
}

int main (int argc, char **argv)
{
	int i,k;
	char fname[64], fname_cin[64], fname_tab[64];
	char tl_sysphrase_file[64] = "tl_sysphrase.bin";
	char tt[128];
	u_char *cmd, *arg;
	hz_input_table InpTable;
	int TotalKeyNum;
	int TotalAssociatePhrase = 0;
	int index;
	u_short CharDef[64];
	u_long freq; 
	long nPhrase;
	int  opt, build_sysphrase = 1;
	char sysdumpfilename[64] = "";
	extern char *optarg;
	extern int optind;

	if (argc<=1) 
	{
		printf ("%s [-o <SysPhrase File>] -c <file.cin> \
					[-d <sysfile dumpname][-n] \n", argv[0]);
		printf ("      -n -- no sysphrase output\n");
		exit (0);
	}

#define ACCEPTOPTS      "no:c:d:"
	while ((opt = getopt(argc, argv, ACCEPTOPTS)) != -1) 
	{
#undef ACCEPTOPTS
		switch (opt) 
		{
			case 'o':
				strcpy (tl_sysphrase_file, optarg);
				break;
			case 'c':
				strcpy (fname, optarg);
				break; 
			case 'd':
				strcpy (sysdumpfilename, optarg);
				break;
			case 'n':
				build_sysphrase = 0;
				break;
			default:
				printf ("Error in Parameter \n");
				exit (0);
		} 
	}

	if (strlen (sysdumpfilename) > 0)
	{
		TL_DumpAllPhrase (pDefaultSysphrase, sysdumpfilename);
		exit (0);
	}

	sprintf (fname_cin, "%s.cin", fname);
	sprintf (fname_tab, "%s.tab", fname);
	if ((fr = fopen (fname_cin, "r")) == NULL ) 
	{
		print_error ("Cannot open %s \n", fname_cin);
		exit (EXIT_FAILURE);
	}

	// If 'tl_sysphrase_file' already exists, get the content out.
	pDefaultSysphrase = LoadSystemPhrase (tl_sysphrase_file, 1);

	printf ("pDefaultSysphrase is %p\n", pDefaultSysphrase);

	// Initialize all variables.
	bzero (&InpTable, sizeof (InpTable));
	bzero (ItemOut, sizeof (ItemOut));
	bzero (MyPhraseItem, sizeof (MyPhraseItem));
	InpTable.item = ItemOut;
	InpTable.pAssociatePhrase = MyPhraseItem;

	printf ("++ Generating binary *.tab file from %s...\n", fname_cin);

	strcpy (InpTable.magic_number, MAGIC_NUMBER);

	////////////////////// Input Method Header Info /////////////////////////
	printf ("++ ++ Parsing Table Header\n");

	cmd_arg (tt, &cmd, &arg);
	if (strcmp (cmd, "%ename") || !(*arg) )
		print_error ("%d:  %%ename english_name  expected", lineno);
	arg [CIN_ENAME_LENGTH-1] = 0;
	strcpy (InpTable.ename, arg);

	cmd_arg (tt, &cmd, &arg);
	if (strcmp (cmd, "%prompt") || !(*arg) )
		print_error ("%d:  %%prompt prompt_name  expected", lineno);
	arg [CIN_CNAME_LENGTH-1] = 0;
	strcpy (InpTable.cname, arg);

	cmd_arg (tt,&cmd, &arg); 
	if (strcmp (cmd, "%selkey") || !(*arg) )
		print_error ("%d:  %%selkey select_key_list expected", lineno);
	strcpy (InpTable.selkey, arg);

	cmd_arg (tt,&cmd, &arg);
	if (strcmp (cmd, "%last_full") || !(*arg)) 
		InpTable.last_full = 1;
	else 
	{
		if (arg[0] == '0') 
			InpTable.last_full = 0;
		else 
			InpTable.last_full = 1;
		cmd_arg (tt, &cmd, &arg);
	}

	if (strcmp (cmd, "%dupsel") || !(*arg) )
		print_error ("%d:  %%dupsel NO of dup sel keys  expected", lineno);
	InpTable.MaxDupSel = atoi (arg);

	////////////////////// Key Name and Key Mapping /////////////////////////
	printf ("++ ++ Generating Keyname Index\n");
	cmd_arg (tt, &cmd, &arg);
	if (strcmp (cmd, "%keyname") && strcmp (arg,"begin"))
		print_error ("%d:  %%keyname begin   expected", lineno);

	TotalKeyNum = 0;
	while (1) 
	{
		cmd_arg (tt, &cmd, &arg);
		// Break if "%keyname end" found
		if (!strcmp (cmd, "%keyname"))
			break;

		k = tolower (cmd[0]);
		if (InpTable.KeyMap[k]) 
			print_error("%d:  key %c is already used",lineno, k);

		InpTable.KeyMap[k] = ++TotalKeyNum;

// FIXME: KeyName is capable for 64 element, we are wasting one of it now.
		if (TotalKeyNum > 63)
			print_error("Error, at most 64 key can be defined!\n");

		strncpy (InpTable.KeyName[TotalKeyNum], arg, 3);
	}

// FIXME: Why should we explicitly set <SPACE> as one of the 'input key'?
//        Is it necessary to do so?
	InpTable.KeyMap[32] = 0;  // <SPACE> = 32
	strncpy (InpTable.KeyName[0], " ", 3);
	TotalKeyNum ++;

	InpTable.TotalKey = TotalKeyNum;    /* include space */

	/////////////////// Character / Code Mapping Table //////////////////////
	printf ("++ ++ Generating Character Mapping Table\n");
	cmd_arg (tt, &cmd, &arg);
	if (strcmp (cmd, "%chardef") || strcmp (arg,"begin"))
		print_error ("%d:  %%chardef begin   expected", lineno);

	index = 0;
	TotalAssociatePhrase = 0;

	while (!feof(fr)) 
	{
		int		len, k, m, n;
		u_long	key1, key2;

		cmd_arg (tt,&cmd,&arg);
		if (!cmd[0] || !arg[0] || !strncmp (arg, "end", 3)) 
			break;

		len = strlen(cmd);
		if (len > InpTable.MaxPress) 
			InpTable.MaxPress=len;

		if (len > 10) 
			print_error ("%d:  only <= 10 keys is allowed", lineno);

		key1 = key2 = 0;
		for (i = 0; i < len; i++) 
		{
			if (i < 5) 
			{
				k = InpTable.KeyMap [cmd [i] ];
				key1 |= k << (24 - i * 6);
			}
			else 
			{
				k = InpTable.KeyMap [cmd [i] ];
				key2 |= k<< (24 - (i - 5) * 6);
			}
		}
		memcpy (&InpTable.item [index].key1, &key1, 4);
		memcpy (&InpTable.item [index].key2, &key2, 4);

		if (TL_MatchPhrase (pDefaultSysphrase, arg, &freq, &nPhrase) == 0)
		{
			nPhrase = TL_AppendPhrase (pDefaultSysphrase, arg);
			TL_GetPhraseFreq (pDefaultSysphrase, nPhrase, &freq);
		}
		InpTable.item[index].nPhrase = nPhrase;
		// InpTable.item[index].frequency = freq;

		// By this, use the arg[0] and arg[1], compute a 16 bit binary integer
		// and use this as an index on 'pAssociatePhrase'
		m = (*arg) * 256 + *(arg+1);
		if (InpTable.pAssociatePhrase[m].total == 0)
		{
			InpTable.pAssociatePhrase[m].pPhrase = 
						(ITEM **) malloc (sizeof (ITEM *) * 5);
			TotalAssociatePhrase ++;
		}
		else if (InpTable.pAssociatePhrase[m].total % 5 == 0)
		{
			int total = InpTable.pAssociatePhrase[m].total + 5;
			ITEM **q = InpTable.pAssociatePhrase[m].pPhrase;
			q = (ITEM **) realloc (q, sizeof (ITEM *) * total);

			// realloc may return and different pointer.
			InpTable.pAssociatePhrase[m].pPhrase = q; 
		}
		n = InpTable.pAssociatePhrase[m].total;
		InpTable.pAssociatePhrase[m].pPhrase[n] = &InpTable.item[index];
		InpTable.pAssociatePhrase[m].total ++;
		index++;
	}
	fclose(fr);

	InpTable.TotalChar = index;
	InpTable.TotalAssociatePhrase = TotalAssociatePhrase;
	SortPhraseItem (pDefaultSysphrase, &InpTable);

	printf ("++ ++ Generating Character Key Index\n");
	/**************** generate 64 index number ***********************/
	bzero (CharDef, sizeof(CharDef));
	for (i = 0; i < index; i++) 
	{
		int kk = InpTable.item[i].key1 >> 24 & 0x3f; 
		if (!CharDef[kk]) 
		{
			InpTable.KeyIndex[kk]= (u_short) i;
			CharDef[kk]=1;
		}
	}

	// Why??
	InpTable.KeyIndex[TotalKeyNum] = index;
	// Why??
	for(i=TotalKeyNum-1; i>0; i--)
	{
		if (!CharDef[i]) 
			InpTable.KeyIndex[i] = InpTable.KeyIndex[i+1];
	}
	printf ("++ ++ Saving TAB File\n");
	SaveLoadInputMethod (&InpTable, fname_tab);

	printf ("++ ++ Saving System Phrase File\n");
	if (build_sysphrase == 1)
		TL_SaveAllPhrase (pDefaultSysphrase, tl_sysphrase_file);

//	printf ("MaxPress is %d\n",InpTable.MaxPress);
//	printf ("MaxDupSel is %d\n",InpTable.MaxDupSel);
	return 0;
}
