
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>
#include <string.h>
#include "xl_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;
}
