/*
 * 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
 *
 * $Id$
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>


#include <xl_pinyin.h>
//#include <xl_hzinput.h>
#define  fatal       printf
#define  message     printf
#define  MIN_USRPHR_SIZE	818

static int LoadPinyinTable (char *pathname);
static int SaveSysPhrase (char *pathname, int remove);
static int SaveUsrPhrase (char *pathname);
static int LoadUsrPhrase (char *pathname);
static int LoadSysPhrase (char *pathname);
static void FindMatchPhrase (InputModule * inmd, PYString pinyin[],
			     int lenpy);
static void SortOutput (InputModule * inmd);
static void FillForwardSelection (InputModule * inmd, int startpos);
static void FillBackwardSelection (InputModule * inmd, int lastpos);
static int AdjustPhraseFreq ();
static void CreatePyMsg (InputModule * inmd);
static int ParsePy (InputModule * inmd, char *pybuf, PYString pinyin[]);

static PinYin pytab[26][MAX_EACH_PY];
      // MAX_EACH_PY = 38    a[], b[], c[] ....
      // map the pinyin to keys
static SysPhrase *sysph[MAX_PY_NUM];	// system phrases
static int sys_size;

static int FuzzyPinyin;		// zh-ch-sh z-c-s
static UsrPhrase *usrph[MAX_PY_NUM];	//user defined phrase

/**************************************************************************
 *                   Structure of the Char/Phrases                        *
 *     u_char len;     // char/phrase len                                 * 
 *     u_char count;   // how many char/phrases in this node              *
 *     u_char key[len+1]; // pinyin key, 1+len encoding                   *
 *     u_char char_freq[];    phrase/freq,phrase/freq pairs               *
 **************************************************************************/

inline u_char *
GetPhrase (ChoiceItem * p, char *pBuf)
{
  char *temp = pBuf;		/* [2*MAX_PHRASE_LEN+1]; */
  int len = (int) (p->head->len);

  strncpy (temp, p->head->key + len + 1 + p->index * (2 * len + 1), 2 * len);
  temp[2 * len] = '\0';
  return temp;
}

inline u_char *
GetFreq (ChoiceItem * p)
{
  int len = (int) (p->head->len);
  return (u_char *) (p->head->key + len + 1 + p->index * (2 * len + 1) +
		     2 * len);
}

/*
static int  ConfigFuzzyPinyin(const char *confstr)
{
    bool value = BoolConf(confstr);
    FuzzyPinyin = value;

    if (value) {
        message("zh/z-ch/c-sh/s fuzzy pinyin enabled.\r\n");
    }
    return SUCCESS;
}
*/

/***************************************************************************
 *                            Init/Cleanup                                 *
 ***************************************************************************/
/* Initiate the InputModule structure */
int
InitPinyinInput (char *szPath)
{
  char buf[256];
  char *usrhome;
  int retval;
  struct stat statbuf;

  sprintf (buf, "%s/%s", szPath, "pinyin.map");
  if ((retval = access (buf, R_OK)) != 0)
    {
      printf ("Couldn't access %s.\n", buf), exit (-1);
    }
  if (LoadPinyinTable (buf) == -1)
    return -1;

  sprintf (buf, "%s/%s", szPath, "sysphrase.tab");
  if ((retval = access (buf, R_OK)) != 0)
    {
      printf ("Couldn't access %s.\n", buf), exit (-1);
    }
  if (LoadSysPhrase (buf) == -1)
    return -1;

  //Rat: modified for processing user-defined dictionaries
  if ((usrhome = getenv ("HOME")) != NULL)
    {
      sprintf (buf, "%s/%s", usrhome, ".pyinput");
      retval = stat (buf, &statbuf);

      if ((retval == 0))
	{
	  if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
	    {
	      sprintf (buf, "%s/%s/%s", usrhome, ".pyinput", "usrphrase.tab");

	      if ( (retval = stat(buf, &statbuf)) == 0)
		{
		  if ( statbuf.st_size < MIN_USRPHR_SIZE || LoadUsrPhrase (buf) == -1)
		    {
		      printf ("Couldn't load %s. Please fix it.\n", buf);
		      sprintf (buf, "%s/%s", szPath, "usrphrase.tab");
		      if ((retval = access (buf, R_OK)) == 0)
			{
			  if (LoadUsrPhrase (buf) == -1)
			    printf ("Couldn't load %s. Please fix it.\n",
				    buf);
			}
		    }
		}
	      else
		{
		  creat (buf, 0600);
		  sprintf (buf, "%s/%s", szPath, "usrphrase.tab");
		  if ((retval = access (buf, R_OK)) == 0)
		    {
		      if (LoadUsrPhrase (buf) == -1)
			printf ("Couldn't load %s. Please fix it.\n", buf);
		    }
		}
	    }
	  else
	    {
	      printf ("Sorry, %s is not a directory.\n", buf);
	    }

	}
      else
	{
	  mkdir (buf, 0700);	//Rat: making $HOME/.pyinput
	  sprintf (buf, "%s/%s/%s", usrhome, ".pyinput", "usrphrase.tab");
	  creat (buf, 0600);	//Rat: making $HOME/.pyinput/usrphrase.tab

	  sprintf (buf, "%s/%s", szPath, "usrphrase.tab");
	  if ((retval = access (buf, R_OK)) == 0)
	    {
	      if (LoadUsrPhrase (buf) == -1)
		{
		  printf ("Couldn't load %s. Please fix it.\n", buf);
		}
	    }
	}
    }
  else
    {
      printf ("Sorry, I couldn't find your $HOME.\n");
      sprintf (buf, "%s/%s", szPath, "usrphrase.tab");
      printf ("Turn to access %s", buf);

      if ((retval = access (buf, R_OK)) != 0)
	printf ("Couldn't access %s.\n", buf);
      else
	{
	  if (LoadUsrPhrase (buf) == -1)
	    printf ("Couldn't load %s.\n", buf);
	}

    }

  return 1;
}

void
PinyinInputCleanup ()
{
  char *usrhome = getenv ("HOME");
  char szFileName[256];

  AdjustPhraseFreq ();		// lower the freq to [0,50)
  if (usrhome != NULL)
    {
      sprintf (szFileName, "%s/%s/%s", usrhome, ".pyinput", "usrphrase.tab");
      SaveUsrPhrase (szFileName);
    }
  else
    {
      printf ("Sorry, couldn't find your $HOME.\n");
    }
}

/*  Reade the pinyin.map, fill the pytab structure */

static int
LoadPinyinTable (char *pathname)
{
  FILE *stream;
  char str[250], strpy[15], strhz[241];
  int i = 0, j = 0, lastpy = 0, curpy;

  if ((stream = fopen (pathname, "r")) == NULL)
    {
      fatal ("%s file not found\n", pathname);
      return -1;
    }
  while (!feof (stream))
    {
      if (fgets (str, 250, stream) != NULL)
	{
	  sscanf (str, "%s %s", strpy, strhz);
	  curpy = strpy[0] - 'a';
	  if (curpy != lastpy)
	    j = 0;
	  strcpy (pytab[curpy][j].py, strpy);
	  pytab[curpy][j].key = i + 1;
	  lastpy = curpy;
	  i++, j++;
	}
    }
#ifdef DEBUG
  warn ("sizeof(pytab=%d\n", sizeof (pytab));
#endif
  fclose (stream);

  return SUCCESS;
}

 /* need to combine the same pinyin/phrases */
static int
SaveUsrPhrase (char *pathname)
{
  int i, tmpcount;
  FILE *out;
  UsrPhrase *p0, *q0;
  u_short total;
  u_char len, *buf, *p;
  long t;

  if ((out = fopen (pathname, "wb")) == NULL)
    fatal ("Couldn't open %s in WriteInBinary mode.\n", pathname), exit (-1);

  if ((buf = malloc (256 * 8)) == NULL)
    fatal ("Not enough memory\n");

  for (i = 1; i < MAX_PY_NUM; i++)
    {
      t = ftell (out);
      total = 0;
      fwrite (&total, sizeof (total), 1, out);
      // first, calculate the different pinyin key phrases
      tmpcount = 0;
      p = buf;
      for (p0 = usrph[i]; p0 != NULL; p0 = p0->next)
	{
	  len = p0->len;
	  for (q0 = usrph[i]; q0 != p0; q0 = q0->next)
	    if (q0->len == len && !memcmp (p0->key, q0->key, len + 1))
	      break;
	  if (p0 == q0)
	    {
	      total++;
	      fwrite (&q0->len, sizeof (q0->len), 1, out);
	      fwrite (&q0->count, sizeof (q0->count), 1, out);
	      fwrite (q0->key, q0->len + 1, 1, out);
	      fwrite (q0->key + q0->len + 1, (2 * q0->len + 1), q0->count,
		      out);
	    }
	}
      if (total == 0)
	continue;

      fseek (out, t, SEEK_SET);
      fwrite (&total, sizeof (total), 1, out);
      fseek (out, 0, SEEK_END);
    }
  t = ftell (out);
  fwrite (&t, sizeof (t), 1, out);
  free (buf);
  fclose (out);

  return 0;
}

int
UnloadUserPhrase ()
{
  int i;
  UsrPhrase *p0, *q0;

  for (i = 1; i < MAX_PY_NUM; i++)
    {
      // first, calculate the different pinyin key phrases
      for (p0 = usrph[i]; p0 != NULL;)
	{
	  q0 = p0;
	  p0 = p0->next;
	  free (q0);
	}
      usrph[i] = NULL;
    }
  return 0;
}

// don't save the frequency information, all lost?
static int
SaveSysPhrase (char *pathname, int remove)
{
  FILE *out;
  char *p;
  if ((out = fopen (pathname, "wb")) == NULL)
    fatal ("%s cant open.\n", pathname);

  p = (char *) (sysph[1]);

  fwrite (p, sys_size, 1, out);
  fwrite (&(sys_size), sizeof (int), 1, out);

  fclose (out);

  if (remove)
    free (p);
  return 0;
}

int
UnloadSysPhrase ()
{
  char *p = (char *) (sysph[1]);
  free (p);
  return 0;
}

static int
LoadUsrPhrase (char *pathname)
{
  FILE *stream;
  UsrPhrase *kph, *tmp;
  int i, j, ahead, fsize;
  u_short count;
  u_char len, size;

  if ((stream = fopen (pathname, "r")) == NULL)
    {
      fatal ("Couldn't open file \"%s\".\n", pathname);
      return -1;
    }

  if (fseek (stream, -4, SEEK_END) == -1 ||
      fread (&fsize, sizeof (int), 1, stream) != 1 ||
      fsize != (ftell (stream) - sizeof(int) ))	// error!!
    {
      //   warn("user file size=%d\n",fsize);
      fatal ("\n\"%s\" is not a valid pinyin phrase file.\n", pathname);
      return -1;
    }

  fseek (stream, 0, SEEK_SET);

  for (i = 1; i < MAX_PY_NUM; i++)
    {
      usrph[i] = NULL;
      if (fread (&count, sizeof (count), 1, stream) != 1)
	{
	  fatal ("Error in reading....\n");
	  return -1;
	}
      if (count == 0)
	continue;
      for (j = 0; j < count; j++)
	{
	  if (fread (&len, sizeof (len), 1, stream) != 1)
	    {
	      fatal ("Error in reading....1\n");
	      return -1;
	    }
	  if (fread (&size, sizeof (size), 1, stream) != 1)
	    {
	      fatal ("Error in reading....2\n");
	      return -1;
	    }
	  if (
	      (kph =
	       (UsrPhrase *) malloc (6 + len + 1 + (2 * len + 1) * size)) ==
	      NULL)
	    {
	      fatal ("Not enough memory\n");
	      return -1;
	    }
	  kph->len = len;
	  kph->count = size;
	  kph->next = NULL;
	  if (fread (kph->key, sizeof (char), len + 1, stream) != len + 1)
	    {
	      fatal ("Error in reading....3\n");
	      return -1;
	    }
	  if (fread (kph->key + len + 1, 2 * len + 1, size, stream) != size)
	    {
	      fatal ("Error in reading....4\n");
	      return -1;
	    }
	  ahead = kph->key[1];
	  ahead |= (kph->key[0] & 0x01) << 8;
	  if (usrph[ahead] == NULL)
	    usrph[ahead] = kph;
	  else
	    {
	      tmp = usrph[ahead];
	      while (tmp->next != NULL)
		tmp = tmp->next;
	      tmp->next = kph;
	    }
	}
    }
  fclose (stream);
  return 0;
}

// Load the system and user phrase library
// the phrase file can be combined just by cat a >> b

static int
LoadSysPhrase (char *pathname)
{
  FILE *stream;
  Phrase *kph;
  SysPhrase *sysph_tmp;
  char *p;
  int i, j;

  if ((stream = fopen (pathname, "rb")) == NULL)
    {
      fatal ("Couldn't open file \"%s\".\n", pathname);
      return -1;
    }

  if (fseek (stream, -4, SEEK_END) == -1 ||
      fread (&(sys_size), sizeof (int), 1, stream) != 1 ||
      sys_size != ftell (stream) - 4)	// error!!
    {
      fatal ("%s is not a valid pinyin phrase file.\n", pathname);
      return -1;
    }
  fseek (stream, 0, SEEK_SET);
  p = (char *) malloc (sys_size);
  memset (p, 0, sys_size);
  /* Attach the shared segment to local address space */
  if (fread (p, sys_size, 1, stream) != 1)
    {
      fatal ("Load File %s Error.\n", pathname);
      return -1;
    }
  for (i = 1; i < MAX_PY_NUM; i++)
    {
      sysph[i] = sysph_tmp = (SysPhrase *) p;
      p = (char *) sysph_tmp->phrase;
      for (j = 0; j < sysph_tmp->count; j++)
	{
	  kph = (Phrase *) p;
	  p += SizeOfPhrase (kph->len, kph->count);	// skip the string 
	}
    }
  fclose (stream);
  return 0;
}

// When loading the phrase library, save it in memory
// structure, dynamic linklist
/* str, hanzi codes, key: pinyin codes, len: length, pass: system/user */
// pass=1 system phrase, pass=0 user phrase(search first) 

static void
SaveUsrPhraseToMem (u_char * str, u_char * key, int len, int freq)
{
  UsrPhrase *kph, *tmp;
  short ahead;

  if (len < 1)
    return;

  ahead = (short) key[1];
  ahead |= (key[0] & 0x01) << 8;

  /* allocate a new one, not combine, will combine during saving */
  if ((kph = (UsrPhrase *) malloc (4 + SizeOfPhrase (len, 1))) == NULL)
    fatal ("Not enough memory\n");

  kph->len = len;
  memcpy (kph->key, key, len + 1);
  kph->count = 1;
  kph->next = NULL;
  memcpy (kph->key + len + 1, str, len * 2);
  kph->key[len + 1 + 2 * len] = freq;

  if (usrph[ahead] == NULL)
    usrph[ahead] = kph;
  else
    {
      tmp = usrph[ahead];
      while (tmp->next != NULL)
	tmp = tmp->next;
      tmp->next = kph;
    }
}

/* freq 0-255, simple algorithm
   save freq each time or don't save ( all set to 0 again?)
  
   make 0-255 -> 0-100 better 
   0-25, keep it
   25-255,  (freq-25)/10+25  25->48
 */

static int
AdjustPhraseFreq ()
{
  UsrPhrase *uph;
  SysPhrase *sysph_tmp;
  Phrase *sph;
  int i, j, k, index;
  char *p;

  for (i = 1; i < MAX_PY_NUM; i++)
    {
      // user phrases
      for (uph = usrph[i]; uph != NULL; uph = uph->next)
	{
	  for (k = 0; k < uph->count; k++)
	    {
	      index = uph->len + 1 + (2 * uph->len + 1) * k + 2 * uph->len;
	      if (uph->key[index] > 25)
		uph->key[index] = 25 + (uph->key[index] - 25) / 10;
	    }
	}

      // system phrases
      sysph_tmp = sysph[i];
      assert (sysph_tmp != NULL);
      p = (char *) sysph_tmp->phrase;	// count = total pinyin number
      for (j = 0; j < sysph_tmp->count; j++)
	{
	  sph = (Phrase *) p;
	  assert (sph != NULL);
	  for (k = 0; k < sph->count; k++)
	    {
	      index = sph->len + 1 + (2 * sph->len + 1) * k + 2 * sph->len;
	      if (sph->key[index] > 25)
		sph->key[index] = 25 + (sph->key[index] - 25) / 10;
	    }
	  p += SizeOfPhrase (sph->len, sph->count);
	}
    }

  return SUCCESS;
}

static int
QueryPhrase (InputModule * inmd, u_char * key, int len)
{
  short ahead;
  UsrPhrase *uph;
  char *p;
  SysPhrase *sysph_tmp;
  Phrase *sph;
  int i;
  u_char phkey[MAX_PHRASE_LEN + 1];
  short mask = 0;
  int j, count = 0;

  if (len < 1)
    return 0;

  ahead = (short) key[1];
  ahead |= (key[0] & 0x01) << 8;

  for (i = 0; i < len; i++)
    mask += 1 << i;
  for (uph = usrph[ahead]; uph != NULL; uph = uph->next)
    {
      if (uph->len < len)
	continue;

      memcpy (phkey, uph->key, len + 1);
      phkey[0] &= mask;
      if (!memcmp (phkey, key, len + 1))	// match
	{
	  if (uph->len == len)	// exact match
	    {
	      inmd->tempsel[len - 1][inmd->seltotal[len - 1]++] =
		(Phrase *) (((char *) uph) + 4);
	    }
	  else
	    count++;		// calculate the phrase longer than len
	}
    }

  // search in user phrase lib first, then system phrase libray 
  sysph_tmp = sysph[ahead];
  p = (char *) sysph_tmp->phrase;	// count = total pinyin number
  assert (p != NULL);
  for (j = 0; j < sysph_tmp->count; j++)
    {
      sph = (Phrase *) p;
      if (sph->len >= len)
	{
	  memcpy (phkey, sph->key, len + 1);
	  phkey[0] &= mask;
	  if (!memcmp (phkey, key, len + 1))	// match
	    {
	      if (sph->len == len)
		inmd->tempsel[len - 1][inmd->seltotal[len - 1]++] = sph;
	      else
		count++;
	    }
	}
      p += SizeOfPhrase (sph->len, sph->count);
    }
  return count;
}

// given key and len, pass out kphs, original kphs = NULL
// a big bug, if the phrase exists in system phrase, then the new user phrase
//  can't be showed 

void
ResetPinyinInput (InputModule * inmd)
{
  *(inmd->inbuf) = '\0';
  *(inmd->inbuftmp) = '\0';
  *(inmd->pybuftmp) = '\0';
  *(inmd->iapybuf) = '\0';
  *(inmd->iahzbuf) = '\0';

  inmd->len = 0;
  inmd->lenpy = 0;
  inmd->pinyinpos = 0;
  inmd->lenkey = 0;
  inmd->key[0] = '\0';
  inmd->nTotalCurSel = 0;
}

// pinyin[0]-pinyin[len-1], parsed pinyin chars
// this routine calculate the a-z beginning and exclude the i/u/v begining

static int
EffectPyNum (PYString pinyin[], int len)
{
  int i;
  char ch;
  int count = 0;

  for (i = 0; i < len; i++)
    {
      ch = pinyin[i][0];
      if (ch == 'i' || ch == 'u' || ch == 'v')
	continue;
      if (ch < 'a' || ch > 'z')
	continue;
      count++;
    }
  return count;
}

/* Added by Arthur Ma, 2000.3.6 */
static char *
szGetSelectPhrase (InputModule * inmd, int n, char *buf)
{
  ChoiceItem *phr = inmd->sel;
  int idx = n;
  char temp[256];

  if (!inmd->len)
    return NULL;

  idx = inmd->startpos + n;

  if (idx > inmd->endpos)
    return NULL;		// out of range selection!
  strcpy (buf, GetPhrase (phr + idx, temp));
  return buf;
}

static int
SelectKeyPressed (InputModule * inmd, char ch, char *strbuf)
{
  ChoiceItem *phr = inmd->sel;

  char *pybuftmp = inmd->pybuftmp;
  /* already selected Hanzi buffer */
  char *inbuftmp = inmd->inbuftmp;
  /* inputed pinyin buffer */
  char temp[2 * MAX_PHRASE_LEN + 1];

  int i, j;
  u_char *fq;
  char strhz[MAX_PHRASE_LEN * 2 + 1];
  int pos, idx;

  if (!inmd->len)
    return 1;

  if (ch == ' ')
    idx = 0;
  else if (ch == '0')
    idx = 9;
  else
    idx = (int) ch - (int) '1';

  idx += inmd->startpos;

  if (idx > inmd->endpos)
    return 1;			// out of range selection!

  strcpy (strhz, GetPhrase (phr + idx, temp));
  strcat (pybuftmp, strhz);
  inmd->key[0] |= phr[idx].head->key[0] << inmd->lenkey;
  for (i = 1; i <= phr[idx].head->len; i++)
    {
      inmd->key[(inmd->lenkey)++ + 1] = phr[idx].head->key[i];
    }
  /* pybuftmp, already selected chars */
  if (strlen (pybuftmp) / 2 ==
      (size_t) EffectPyNum (inmd->pinyin, inmd->lenpy))
    {
      if (strlen (strhz) == strlen (pybuftmp))
	{
	  fq = GetFreq (phr + idx);
	  if (*fq < 250)
	    (*fq)++;
	  /* strhz is the last phrase/char, equal, existing phrase/char
	     increase its frequency */
	}
      else if (strlen (pybuftmp) > 2)
	{
	  SaveUsrPhraseToMem (pybuftmp, inmd->key, strlen (pybuftmp) / 2, 1);
	  // not equal and pybuftmp, save the new phrase, 0 is user phrase
	}
      strcpy (strbuf, pybuftmp);

      ResetPinyinInput (inmd);
      return 2;
      /* All the pinyin are translated to char/phrases!  */
    }
  else				// not yet, some unselected pinyin exist
    {
      // forward the pinyinpos pointer
      for (pos = strlen (strhz) / 2; pos > 0; inmd->pinyinpos++)
	{
	  ch = inmd->pinyin[inmd->pinyinpos][0];
	  if (ch == 'i' || ch == 'u' || ch == 'v' || ch < 'a' || ch > 'z')
	    continue;
	  pos--;
	}

      FindMatchPhrase (inmd, inmd->pinyin + inmd->pinyinpos,
		       inmd->lenpy - inmd->pinyinpos);

      FillForwardSelection (inmd, 0);

      *inbuftmp = '\0';		// put the rest of the pinyin into inbuftmp
      for (j = inmd->pinyinpos; j < inmd->lenpy; j++)
	strcat (inbuftmp, inmd->pinyin[j]);

      CreatePyMsg (inmd);
      return 1;
    }
}

/* Always return 0, input pinyin, no key output available */
static int
PinyinKeyPressed (InputModule * inmd, char ch, char *strbuf)
{
  /* parameter strbuf is the newly inputed pinyin, inbuf the
     is the whole inputed pinyin, inbuftmp is the unselected pinyin */

  char *inbuf = inmd->inbuf;
  char *pybuftmp = inmd->pybuftmp;
  /* already selected Hanzi buffer */
  char *inbuftmp = inmd->inbuftmp;
  /* inputed pinyin buffer */

  char chtmp;
  int count;

  /* \010 = Ctrl+H, \177 = BackSpace */
  if (ch == '\010' || ch == '\177')	// BackSpace
    {
      if (!strlen (inbuf))
	return 0;
      else if (!strlen (inbuftmp))
	{
	  strcpy (inbuftmp, inbuf);
	  *pybuftmp = '\0';	// clear all the selected chars, reparse
	}
      else
	{
	  inbuf[strlen (inbuf) - 1] = '\0';
	  inbuftmp[strlen (inbuftmp) - 1] = '\0';	// cut one pinyin-char off
	  if (!strlen (inbuf))
	    {
	      ResetPinyinInput (inmd);
	      return 1;		// mark that we will clear all, refresh
	    }
	}
    }
  else				//other than BackSpace, ch = a-z or '
    {
      strcat (inbuf, strbuf);
      strcat (inbuftmp, strbuf);
    }

  if (!strlen (pybuftmp))
    inmd->pinyinpos = 0;
  /* first pinyin char */

  // parse the unselected pinyin(inbuftmp) input
  count = ParsePy (inmd, inbuftmp, inmd->pinyin + inmd->pinyinpos);
  inmd->lenpy = inmd->pinyinpos + count;

  /* exclude the last i/u/v-beginning pinyin */
  if (inmd->lenpy > 0)
    {
      chtmp = inmd->pinyin[inmd->lenpy - 1][0];
      if (chtmp == 'i' || chtmp == 'u' || chtmp == 'v')
	{
	  inbuf[strlen (inbuf) - 1] = '\0';
	  inbuftmp[strlen (inbuftmp) - 1] = '\0';
	  inmd->lenpy--;
	  return 1;
	}
    }
  /* Too many chars now */
  if (EffectPyNum (inmd->pinyin, inmd->lenpy) > MAX_PHRASE_LEN)
    {
      inbuf[strlen (inbuf) - 1] = '\0';
      inbuftmp[strlen (inbuftmp) - 1] = '\0';
      inmd->lenpy--;
      return 1;
    }
  FindMatchPhrase (inmd, inmd->pinyin + inmd->pinyinpos,
		   inmd->lenpy - inmd->pinyinpos);
  FillForwardSelection (inmd, 0);
  CreatePyMsg (inmd);

  return 1;
}

static int
PinyinParseInput (InputModule * inmd, char ch, char *strbuf)
{
//  if ( (ch>='a' && ch<='z') || ch=='\''|| ch=='\010' || ch=='\177') 
  if ((ch >= 'a' && ch <= 'z') || (ch == '\'' && inmd->inbuf[0] != 0)
      || ch == '\010' || ch == '\177')
    return PinyinKeyPressed (inmd, ch, strbuf);

  if (!strlen (inmd->inbuf))
    return 0;

  switch (ch)
    {
    case '=':
    case '>':
    case ']':
    case '.':			// Select Forward
      FillForwardSelection (inmd, inmd->endpos + 1);
      return 1;
    case '-':
    case '<':
    case '[':
    case ',':			// Select Backward
      FillBackwardSelection (inmd, inmd->startpos - 1);
      return 1;
    case '\033':		//ESCAPE
/*    case 0x09: *//* tab key */
      if (inmd->len != 0)
	{
	  ResetPinyinInput (inmd);
	  return 1;
	}
      return 0;
    case '\r':
      break;

    default:			// select some keys
      if ((ch >= '1' && ch <= '9') || ch == '0' || ch == ' ')
	return SelectKeyPressed (inmd, ch, strbuf);
      break;
    }
  return 0;
}

/* pybuf, current inputed unselected pinyin buffer, MAX_PY_LEN=7 */
/* return the total chars parsed ? */

static int
ParsePy (InputModule * inmd, char *pybuf, PYString pinyin[])
{
  int len, ahead, i, total = 0;
  int offset = 0, count, valid;

  len = strlen (pybuf);
  if (len < 1 || len > MAX_PHRASE_LEN * (MAX_PY_LEN + 1))
    return 0;

  count = 2;			/* 1 always valid */
  while (offset + count <= len)
    {
      if (pybuf[offset] == '\'')	// delimitor ' 
	{
	  strcpy (pinyin[total++], "\'");
	  offset++;
	  count = 2;
	  continue;
	}

      ahead = pybuf[offset] - 'a';
      if (ahead < 0 || ahead > 25)
	return 0;

      // test if this is a valid pinyin prefix
      valid = 0;
      for (i = 0; pytab[ahead][i].key; i++)
	{
	  if (!strncmp (pytab[ahead][i].py, pybuf + offset, count))
	    {
	      valid = 1;
	      break;
	    }
	}

      if (valid)
	count++;
      else
	{
	  strncpy (pinyin[total], pybuf + offset, count - 1);
	  pinyin[total++][count - 1] = '\0';
	  offset += count - 1;
	  count = 2;
	}
    }

  // copy the remaining pinyin
  if (offset < len)
    {
      strncpy (pinyin[total], pybuf + offset, count - 1);
      pinyin[total++][count - 1] = '\0';
    }
  return total;
}

/* create char/phrase according to the pinyin, lenpy is the length of
 pinyin array */

static void
FindMatchPhrase (InputModule * inmd, PYString pinyin[], int lenpy)
{
  int lenkey, keytmp;
  int i, j, k;
  int ahead, tmplen, count = 0;
  int pykey[MAX_PHRASE_LEN][MAX_EACH_PY + 1];
  // MAX_PHRASE_LEN=6, MAX_EACH_PY = 38, a[], b[]
  u_char py[MAX_PY_LEN + 2];
  u_char key[MAX_PHRASE_LEN + 1];
  u_char keyarr[MAX_PHRASE_LEN][500][MAX_PHRASE_LEN + 1];
  // temporary array, 500 items

  int lenarr[MAX_PHRASE_LEN], result;
  char ch;

  if (!lenpy)
    {
      inmd->len = 0;
      return;
    }

  /* first of all, fill the pykey array */
  for (i = 0; i < lenpy; i++)
    {
      ch = pinyin[i][0];
      if (ch == 'i' || ch == 'u' || ch == 'v' || ch < 'a' || ch > 'z')
	continue;		// ignore the i/u/v beginning and non a-z 

      ahead = pinyin[i][0] - 'a';
      lenkey = 0;
      tmplen = strlen (pinyin[i]);
      for (j = 0; (keytmp = pytab[ahead][j].key); j++)
	{
	  if (tmplen == 1 || !strncmp (pinyin[i], pytab[ahead][j].py, tmplen))
	    // prefix match
	    {
	      pykey[count][lenkey++] = keytmp;
	      continue;
	    }
	  else if (FuzzyPinyin && (ch == 'z' || ch == 'c' || ch == 's'))
	    {
	      if (pinyin[i][1] != 'h')
		{
		  strcpy (py + 1, pinyin[i]);
		  py[0] = py[1];
		  py[1] = 'h';
		}
	      else
		{
		  strcpy (py, pinyin[i] + 1);
		  py[0] = ch;
		}
	      if (!strncmp (py, pytab[ahead][j].py, strlen (py)))
		pykey[count][lenkey++] = keytmp;
	    }
	}
      pykey[count++][lenkey] = 0;
    }				// for i = 1 to lenpy, pykey array filled

  for (i = 0; i < MAX_PHRASE_LEN; i++)
    lenarr[i] = 0;

  for (i = 0; i < 8; i++)
    inmd->seltotal[i] = 0;

  /* for the first char */
  for (k = 0; pykey[0][k]; k++)
    {
      key[1] = pykey[0][k] & 0xff;
      key[0] = '\0';
      key[0] |= (pykey[0][k] & 0x0100) >> 8;
      //  single char phrase
      result = QueryPhrase (inmd, key, 1);
      if (result > 0)		//save the possible multiple-char phrases 
	memcpy (keyarr[0][lenarr[0]++], key, 2);
    }
  /* count is the real pinyin number, parse the remaining */
  for (i = 1; i < count; i++)
    for (j = 0; j < lenarr[i - 1]; j++)	// for all the possible phrase match
      for (k = 0; pykey[i][k]; k++)
	{
	  memcpy (key, keyarr[i - 1][j], i + 1);
	  key[i + 1] = pykey[i][k] & 0xff;
	  key[0] |= (pykey[i][k] & 0x0100) >> (8 - i);

	  result = QueryPhrase (inmd, key, i + 1);
	  if (result > 0)
	    memcpy (keyarr[i][lenarr[i]++], key, i + 2);
	}

  SortOutput (inmd);
}

static void
SortOutput (InputModule * inmd)
{
  int i, j, k, lenph, totalph = 0;
  ChoiceItem phtmp, *ph = inmd->sel;

  totalph = 0;
  for (i = MAX_PHRASE_LEN - 1; i >= 0; i--)
    {
      lenph = 0;
      // warn("seltotal[%d]=%d",i,seltotal[i]);
      for (j = 0; j < inmd->seltotal[i]; j++)
	{
	  for (k = 0; k < inmd->tempsel[i][j]->count; k++)
	    {
	      inmd->sel[totalph + lenph].head = inmd->tempsel[i][j];
	      inmd->sel[totalph + lenph].index = k;
	      lenph++;
	    }
	}

      // sort the phrases 
      for (k = 0; k < lenph; k++)
	for (j = k; j < lenph; j++)
	  if (*GetFreq (ph + k) < *GetFreq (ph + j))
	    {
	      phtmp = ph[k];
	      ph[k] = ph[j];
	      ph[j] = phtmp;
	    }

      totalph += lenph;
      ph += lenph;
    }				//end of for loop

  inmd->len = totalph;		// total possible phrase selection
}

/* startpos = 0 -> len-1 */
static void
FillForwardSelection (InputModule * inmd, int startpos)
{
  char *iahzbuf = inmd->iahzbuf;
  char temp[2 * MAX_PHRASE_LEN + 1];

  int i, count;
  int SelAreaWidth = inmd->SelectionLen;
  // GetScreenWidth() - 12 - PINYIN_AREA_WIDTH;
  char strtmp[2 * MAX_PHRASE_LEN + 10];

  if (startpos > inmd->len - 1 || startpos < 0)
    return;			// non-forwardable, keep the iahzbuf intact
  iahzbuf[0] = '\0';
  if (inmd->len < 1)
    return;			// clear the iahzbuf

  count = 0;			// backup the starting position
  inmd->startpos = startpos;
  inmd->endpos = startpos - 1;
  if (inmd->startpos > 0)
    sprintf (inmd->iahzbuf, "< ");
  else
    sprintf (inmd->iahzbuf, "  ");

  while (inmd->endpos < inmd->len - 1 && count < 10)
    {
      sprintf (strtmp, "%d%s ", (count + 1) % 10,
	       GetPhrase (inmd->sel + inmd->endpos + 1, temp));

      if ((strlen (iahzbuf) + strlen (strtmp) + 2) <= (u_int) SelAreaWidth)
	{
	  strcat (iahzbuf, strtmp);
	  inmd->endpos++;
	  count++;
	}
      else
	break;
    }
  inmd->nTotalCurSel = count;
  if (inmd->endpos < inmd->len - 1 && count >= 1)
    {
      for (i = strlen (iahzbuf); i < SelAreaWidth - 2; i++)
	iahzbuf[i] = ' ';
      iahzbuf[SelAreaWidth - 2] = '>';
      iahzbuf[SelAreaWidth - 1] = '\0';
      //  strncat(iahzbuf,strspace,SelAreaWidth-strlen(iahzbuf)-2);
      //  strcat(iahzbuf,">");
    }
}

static void
FillBackwardSelection (InputModule * inmd, int lastpos)
{
  char *iahzbuf = inmd->iahzbuf;
  char temp[2 * MAX_PHRASE_LEN + 1];

  int SelAreaWidth = inmd->SelectionLen;
  //GetScreenWidth() - 10 - PINYIN_AREA_WIDTH;

  int count, ialen;
  char strbuf[2 * MAX_PHRASE_LEN + 10];

  if (lastpos < 0 || lastpos > inmd->len - 1)
    return;			// iahzbuf intact
  iahzbuf[0] = '\0';
  if (inmd->len < 1)
    return;			// clear iahzbuf

  count = 0;
  inmd->endpos = lastpos;
  ialen = 2;			// leftmost "< " or "  "
  inmd->startpos = lastpos + 1;
  while (inmd->startpos > 0 && count < 10)
    {
      strcpy (strbuf, GetPhrase (inmd->sel + inmd->startpos - 1, temp));
      ialen += strlen (strbuf) + 2;
      if (ialen + 2 > SelAreaWidth)
	break;
      count++;
      inmd->startpos--;
    }
  FillForwardSelection (inmd, inmd->startpos);
}

/* PY Area: User's PinYin string */
/* hzstr, the prefix HZ string, str: destination string */

static void
CreatePyMsg (InputModule * inmd)
{
  int i;

  strcpy (inmd->iapybuf, inmd->pybuftmp);

  for (i = inmd->pinyinpos; i < inmd->lenpy; i++)
    {
      strcat (inmd->iapybuf, inmd->pinyin[i]);	// MAX_PY_LEN = 7
      if (inmd->pinyin[i + 1][0] == '\'' || inmd->pinyin[i][0] == '\'')
	continue;
/*
      else
	strcat (inmd->iapybuf, " ");
*/
    }
}

void
Pinyin_SaveAllPyUsrPhrase ()
{
  PinyinInputCleanup ();
}

static int count = 0;
InputModule *
pCCE_OpenPinyin (char *szPath)
{
  InputModule *p;
  if (count == 0)
    {
      if (InitPinyinInput (szPath) == -1)
	return NULL;
    }
  p = (InputModule *) malloc (sizeof (InputModule));
  if (p != NULL)
    ResetPinyinInput (p);
  p->SelectionLen = 80 - 12 - PINYIN_AREA_WIDTH;
  return p;
}

void
CCE_ClosePinyin (InputModule * p)
{
  count--;
  if (count == 0)
    {
      UnloadSysPhrase ();
      UnloadUserPhrase ();
    }
  Pinyin_SaveAllPyUsrPhrase ();
  free (p);
}

int
Pinyin_KeyFilter (InputModule * pClient, u_char key, char *buf, int *len)
{
  int r;

  buf[0] = key;
  buf[1] = '\0';
  r = PinyinParseInput (pClient, key, buf);

  switch (r)
    {
    case -1:
      return 0;

    case 2:
      *len = strlen (buf);
      return 2;

    case 1:
    case 0:
      break;

    default:
      printf ("r = %d\n", r);
      assert (false);
      break;
    }
  return r;
}

int
Pinyin_KeyPressed (InputModule * pClient, u_char key)
{
  char buf[40];
  int count;

  buf[0] = key;
  buf[1] = '\0';

  count = PinyinParseInput (pClient, key, buf);
  if (count == -1)

    return 1;
  else if (count != 1)
    return pClient->nTotalCurSel;
  return 0;
}

char *
Pinyin_szGetSelItem (InputModule * pClient, int n, char *buf)
{
  if (n >= 0 && n < pClient->nTotalCurSel)
    {
      return szGetSelectPhrase (pClient, n, buf);
    }
  return NULL;
}

int
Pinyin_ConfigureInputArea (InputModule * p, int SelectionLen0)
{
  p->SelectionLen = SelectionLen0;
  return 1;
}

int
CCE_GetInputDisplay (InputModule * p, char *buf)
{
  strcpy (buf, p->iapybuf);
  return 1;
}

int
CCE_GetSelectDisplay (InputModule * p, char *buf)
{
  strcpy (buf, p->iahzbuf);
  return p->nTotalCurSel;
}
