
#include <stdio.h>
#include <stdarg.h> 
#include <sys/types.h>
#include <string.h>
#include <hzinput.h>
#include "safestring.h"

FILE *fr, *fw;
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);
}

char *skip_space(char *s)
{
	while ((*s==' ' || *s=='\t') && *s) s++;
	return s;
}

char *to_space(char *s)
{
	while (*s!=' ' && *s!='\t' && *s) 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;
}

typedef struct {
	u_long key1;
	u_long key2;
	u_short ch;
	u_short occur_seq;
} ITEM2;

#define MAXSIZE 50000
 /* maximum char/phrase can be defined */

ITEM2 ItemTable[MAXSIZE], ItemTmp[MAXSIZE];
ITEM  ItemOut[MAXSIZE];
int  PhraseIndex[MAXSIZE];
char PhraseBuffer[250000];  /* max 250K phrase buffer */ 
char fname[FILENAME_MAX+1], fname_cin[FILENAME_MAX+1],  /* ANSI C limits */
     fname_dat[FILENAME_MAX+1], fname_idx[FILENAME_MAX+1];

/* qcmp2 compare two ITEM2 structure, according to its key1/key2/ch */
int qcmp2(ITEM2* a, ITEM2* b)
{
	if (a->key1 > b->key1) return 1;
	if (a->key1 < b->key1) return -1;
	if (a->key2 > b->key2) return 1;
	if (a->key2 < b->key2) return -1;
	if (a->ch > b->ch) return 1;
	if (a->ch < b->ch) return -1;
        /* duplicate char, we will elimintate it later */
	return 0;
}

/* qcmp compare two ITEM2 structure, according to its key1/key2/occur_seq */
int qcmp(ITEM2* a, ITEM2* b)
{
	if (a->key1 > b->key1) return 1;
	if (a->key1 < b->key1) return -1;
	if (a->key2 > b->key2) return 1;
	if (a->key2 < b->key2) return -1;
	return (int)a->occur_seq - (int)b->occur_seq;
}

int qcmp_ser(ITEM *a, ITEM* b)
{
	if (a->ch > b->ch) return 1;
	if (a->ch < b->ch) return -1;
	if (a->key1 > b->key1) return 1;
	if (a->key1 < b->key1) return -1;
	if (a->key2 > b->key2) return 1;
	if (a->key2 < b->key2) return -1;
	return 0;
}

void load_phrase( int index, char *buf, int n)
{
  int len = PhraseIndex[index+1] - PhraseIndex[index];

  if (len > n) {
      print_error("buffer overrun %d > %d\n", len, n);
      abort();
  }
  if ( len > 128 || len <= 0) {
     print_error("Phrase error at index %d\n" , index);
     strncpy( buf, "error", n);
     return;
  }
  memcpy(buf,PhraseBuffer+PhraseIndex[index],len);
}

void GenerateDatIdx(hz_input_table *table)
{
   int i,j,k;
   char key[20];
   static char buf[700000];  // 700K *.dat buffer
   int offset;        // buffer offset
   int MaxKeyLen,HasOneChar;
   FILE *fw;

   if ((fw=fopen(fname_idx,"w"))==NULL)
   {
        printf("Cannot create %s\n",fname_idx);
        exit(1);
   }

   MaxKeyLen = table->MaxPress;
   if (MaxKeyLen > 8) MaxKeyLen = 8;
   printf("Max key press is %d\n", MaxKeyLen);
   offset = 0;

   i = 0;
   while(i< table->TotalChar)
   {
     for(j = 0; j < 5; j++)
     {
       key[j] = (ItemOut[i].key1 >> (24 - 6 * j)) & 0x3f;
       key[5+j] = (ItemOut[i].key2 >> (24 - 6 * j)) & 0x3f;
     }
     for(j = 0; j < 10; j++)
     {
       if (key[j] < 0 || key[j] > 60) printf("Error Key index!\n");
       key[j] = table->KeyName[key[j]];
     }
     key[10] = '\0';
     /* now get the key */

     fwrite(key,MaxKeyLen,1,fw);
     fwrite(&offset,sizeof(int),1,fw);
     /* write the key and offset */

     HasOneChar = 0;
     k = i;

     while (k < table->TotalChar)
     {
        if (ItemOut[k].ch >= 0xA1A1)  /* a char */
        {
           if (HasOneChar == 0)
           {
                HasOneChar = 1;
                buf[offset++] = '1';
           }
           buf[offset++] = (ItemOut[k].ch & 0xFF);
           buf[offset++] = (ItemOut[k].ch  >> 8);
        }

        if ( k == table->TotalChar-1 ||
             ItemOut[k].key1 != ItemOut[k+1].key1 ||
             (ItemOut[k].key2 & 0xFFFFF000) != (ItemOut[k+1].key2 &
                 0xFFFFF000))
           break;  //now from i to k is the same keyname
        k++;
     }

     for(j = i; j <= k ; j++)
        if (ItemOut[j].ch < 0xA1A1)  /* a phrase */
        {
            buf[offset++] = '2';
            load_phrase( ItemOut[j].ch, buf+offset, sizeof(buf)-offset);
            offset += strlen(buf+offset);
        }
      buf[offset++]= 0x0A;

      i = k+1;
    }

  fclose(fw);

  if ((fw=fopen(fname_dat,"w"))==NULL)
  {
        printf("Cannot create %s\n",fname_dat);
        exit(1);
  }
  fwrite(buf,offset,1,fw);
  fclose(fw);
}

int main(int argc, char **argv)
{
int i,k;
char tt[128];
hz_input_table InpTable;
u_char *cmd, *arg;
int TotalKeyNum;
ITEM LastItem;
int index,ItemCount;
u_short CharDef[64];
int phrase_count=0, phrasebuf_pointer=0;

if (argc<=1) 
{
	printf("Enter table file name [.cin] : ");
	fgets(fname, FILENAME_MAX+1-4+1, stdin);
	strtok(fname, "\n");  /* Drop the possible final LF character */
	/* fname[] and fname_* will be appended {.cin,.idx,.dat} suffix */
}
else safe_strncpy(fname, argv[1], FILENAME_MAX+1-4);

strcpy(fname_cin,fname);
strcpy(fname_idx,fname);
strcpy(fname_dat,fname);
strcat(fname_cin,".cin");
strcat(fname_idx,".idx");
strcat(fname_dat,".dat");

if ((fr=fopen(fname_cin,"r"))==NULL ) 
	print_error("Cannot open %s \n", fname_cin);

bzero(&InpTable,sizeof(InpTable));
bzero(ItemTable,sizeof(ItemTable));
bzero(ItemOut,sizeof(ItemOut));

printf("Generating binary *.dat file for input method %s...\n",fname_cin);

/****************** Now some basic information ************************/

strcpy(InpTable.magic_number,MAGIC_NUMBER);
cmd_arg(tt, &cmd, &arg);
if (strcmp(cmd,"%ename") || !(*arg) )
	print_error("%d:  %%ename english_name  expected", lineno);
safe_strncpy(InpTable.ename, arg, sizeof (InpTable.ename));

cmd_arg(tt, &cmd, &arg);
if (strcmp(cmd,"%prompt") || !(*arg) )
	print_error("%d:  %%prompt prompt_name  expected", lineno);
safe_strncpy(InpTable.cname, arg, sizeof (InpTable.cname));

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

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);

/******************* now the keyname ****************************/

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);
	if (!strcmp(cmd,"%keyname")) break;
	k = tolower(cmd[0]);  /* k = char */
	if (InpTable.KeyMap[k]) 
            print_error("%d:  key %c is already used",lineno, k);

	InpTable.KeyMap[k] = ++TotalKeyNum;

        if (TotalKeyNum > 63)
            print_error("Error, at most 64 key can be defined!\n");
	InpTable.KeyName[TotalKeyNum] = arg[0];
}

InpTable.KeyMap[32] = 0;  /* SPACE = 32 */
InpTable.KeyName[0] = ' ';
TotalKeyNum++;
InpTable.TotalKey = TotalKeyNum;    /* include space */

/************************ now the character/phrase ***********************/

cmd_arg(tt,&cmd, &arg);
index=0;
while (!feof(fr)) 
{
	int len;
	u_long key1,key2;
	int k;

	cmd_arg(tt,&cmd,&arg);
	if (!cmd[0] || !arg[0]) break;
	len = strlen(cmd);
	if (len > InpTable.MaxPress) InpTable.MaxPress=len;
	if (len > 10) print_error("%d:  only <= 10 keys is allowed",lineno);

	key1=0;
	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(&ItemTable[index].key1,&key1,4);
	memcpy(&ItemTable[index].key2,&key2,4);

        /* is it a chinese character(GB), or phrase ? */	
        len = strlen(arg);
        if (len==2 && (int)(*arg) > 0xA0 && (int)(*(arg+1)) > 0xA0) 
		memcpy(&ItemTable[index].ch,arg,2);
        else 
        {
                int m;
		ItemTable[index].ch=phrase_count;
                  /* ch < 0xA1A1, phrase index */
		PhraseIndex[phrase_count]=phrasebuf_pointer;
		strncpy(&PhraseBuffer[phrasebuf_pointer],arg,sizeof(PhraseBuffer) - phrasebuf_pointer - 1);
		phrasebuf_pointer += len;
                phrase_count++;
	}
	ItemTable[index].occur_seq=index;
	index++;
}
fclose(fr);

if (phrase_count > 0 ) 
{
        int i,t,s;
	PhraseIndex[phrase_count++]=phrasebuf_pointer;
        printf("Phrase Count = %d ", phrase_count);
        /* don't write the phr file, write idx/dat */
        /*
	if ((fw=fopen(fname_phr,"w"))==NULL)
		print_error("Cannot create %s\n", fname_phr);
	fwrite(&phrase_count,4,1,fw);
	fwrite(PhraseIndex,4,phrase_count,fw);
	fwrite(PhraseBuffer,1,phrasebuf_pointer,fw);
	fclose(fw);
        */
}
InpTable.PhraseNum = phrase_count;
InpTable.TotalChar = index;
qsort(ItemTable,index,sizeof(ITEM2),qcmp2);

/******************** eliminate the dupplicated char *******************/
bzero(&LastItem,sizeof(ITEM));
ItemCount=0;
for(i=0;i<index;i++) 
{
       if (memcmp(&ItemTable[i], &LastItem, sizeof(ITEM))) 
       {
  	    memcpy(&ItemTmp[ItemCount++],&ItemTable[i],sizeof(ITEM2));
	    memcpy(&LastItem,&ItemTable[i],sizeof(ITEM));
	}
}

printf("Total Item = %d\n\n", ItemCount);
index=ItemCount;
qsort(ItemTmp,index,sizeof(ITEM2),qcmp);
/* sorting the char/phrase according its key & appearance ordr */

/* now eliminate the occurance field , ITEM2->ITEM */
for(i=0;i<index;i++)
{
     memcpy(&ItemOut[i],&ItemTmp[i],sizeof(ITEM));
}

/******************* generate 64 index number ***********************/

bzero(CharDef,sizeof(CharDef));
for(i=0;i<index;i++) 
{
    int kk=(ItemOut[i].key1 >> 24) & 0x3f; 
    if (!CharDef[kk]) 
    {
        InpTable.KeyIndex[kk]=(u_short)i;
        CharDef[kk]=1;
    }
}

InpTable.KeyIndex[TotalKeyNum]=index;
for(i=TotalKeyNum-1; i>0; i--)
	if (!CharDef[i]) InpTable.KeyIndex[i] = InpTable.KeyIndex[i+1];

/*  Don't write the tab & phr file, write dat/idx file */
/*
if ((fw=fopen(fname_tab,"w"))==NULL) {
	print_error("Cannot create");
}

fwrite(&InpTable,1,sizeof(InpTable),fw);
fwrite(ItemOut,sizeof(ITEM),index, fw);
fclose(fw);
*/

 GenerateDatIdx(&InpTable);

return 0;
}
