/*
 *
 * 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 <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h> 
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>

// #include        <xl_defs.h>
// #include        <xl_system.h>
// #include        <xl_getcap.h>
// #include        <xl_errors.h>
// #include        <xl_hzfb.h>
#include        <xl_pinyin.h>
#include        <xl_hzinput.h>
#define         fatal       printf
#define         message     printf

static int LoadPinyinTable(char* pathname);
static int SaveSysPhrase(char *pathname,int remove);
static int LoadUsrPhrase(char *pathname);
static int LoadSysPhrase(char *pathname);
static int LoadPhraseFrequency(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, sys_num;

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];
  struct stat st;

  sprintf (buf, "%s%s", szPath, "/pinyin.map");
  if (LoadPinyinTable (buf) == -1)
      return -1;

  sprintf (buf, "%s%s", szPath, "/sysphrase.tab");
  if (LoadSysPhrase (buf) == -1)
      return -1;

/*  sprintf (buf, "%s%s", szPath, "/usrphrase.tab");
  if (LoadUsrPhrase (buf) == -1)
      return -1; */
  sprintf(buf, "%s/.pyinput", getenv("HOME"));
  if(stat(buf, &st) == -1) mkdir(buf, 0755);
  sprintf(buf, "%s/.pyinput/usrphrase.tab", getenv("HOME"));
  if(stat(buf, &st) == -1) 
  {
      creat(buf, 0740);
      sprintf (buf, "%s%s", szPath, "/usrphrase.tab");
  }
  LoadUsrPhrase (buf);

  sprintf(buf, "%s/.pyinput/sysfrequency.tab", getenv("HOME"));
  if(stat(buf, &st) == -1 || LoadPhraseFrequency(buf)==-1) 
  {
      creat(buf, 0740);
      SavePhraseFrequency(buf);
  }
  return 1;
}

void PinyinInputCleanup ()
{
  AdjustPhraseFreq ();  // lower the freq to [0,50)
  /* SaveSysPhrase (UNICON_LIB "/sysphrase.tab", TRUE); */
  //SaveUsrPhrase (szFileName);
}

/*  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 */
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("Not enough memory\n");
   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;
}

int SavePhraseFrequency(char *pathname)
{
  FILE *stream;
  Phrase *sph;
  SysPhrase *sysph_tmp;
  char *f;
  char *p;
  int i,j,k,index,pcount;

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

  f = (char *) malloc (sys_num);
  memset (f, 0, sys_num);
  pcount=0;

  for(i = 1; i < MAX_PY_NUM; i++)
  {
     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;
            f[pcount]=sph->key[index];
            pcount++;
        }   
        p += SizeOfPhrase(sph->len,sph->count);
     }
  }
  assert (pcount==sys_num);
  fseek(stream,0,SEEK_SET);
  fwrite(f, sys_num, 1, stream); 
  fwrite(&(sys_size),sizeof(int),1,stream);
  fwrite(&(sys_num),sizeof(int),1,stream);
  fclose(stream);
  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("%s file can't open\n",pathname);
         return -1;
   }
   if (fseek(stream,-4,SEEK_END) == -1 ||
       fread(&fsize,sizeof(int),1,stream) != 1 ||
       fsize != ftell(stream)-4)   // error!!
   {
    //   warn("user file size=%d\n",fsize);
       fatal("%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("%s file can't open\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);
  sys_num = 0;
  /* 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 
        sys_num +=kph->count;
     }
  }
  fclose(stream);
  return 0;
}

static int LoadPhraseFrequency(char *pathname)
{
  FILE *stream;
  Phrase *sph;
  SysPhrase *sysph_tmp;
  char *f;
  char *p;
  int i,j,k,index,sys_size_tmp,sys_num_tmp,pcount;

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

  if (fseek(stream,-sizeof(int)*2,SEEK_END) == -1 || 
      fread(&(sys_size_tmp),sizeof(int),1,stream) != 1 ||
      fread(&(sys_num_tmp),sizeof(int),1,stream) != 1 ||
      sys_size != sys_size_tmp||   
      sys_num_tmp != ftell(stream)-sizeof(int)*2 ||
      sys_num != sys_num_tmp)   // error!!
  {
      fatal("%s is not a valid pinyin phrase freqency file.\n",pathname);
      return -1;
  }
  fseek(stream,0,SEEK_SET);
  f = (char *) malloc (sys_num);
  memset (f, 0, sys_num);
  if (fread(f, sys_num, 1, stream) != 1)
  {
      fatal("Load File %s Error.\n", pathname);
      return -1;
  }
  //
  pcount=0;
  for(i = 1; i < MAX_PY_NUM; i++)
  {
     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;
            sph->key[index] = f[pcount];
            pcount++;
        }   
        p += SizeOfPhrase(sph->len,sph->count);
     }
  }
  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;
  inmd->flg_english = 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;

    printf("szGetSelectPhrase called\n");
    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 (ch == '\n')
  {
      strcpy(strbuf,inmd->inbuf);
      ResetPinyinInput(inmd);
      return 2;
  }

  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
  {
      inmd->flg_english = 0;
      // 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') 
         {
             inmd->flg_english = 1;
             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;
  int i;
  char tmpbuf[128];

 /* \010 = Ctrl+H, \177 = BackSpace */
  if (ch == '\010' || ch == '\177')  // BackSpace
  {
      if (!strlen(inbuf)) 
           return 0;
      else if (!strlen(inbuftmp))
      {
          strcpy(inbuftmp,inbuf);
          inbuf[strlen(inbuf)-1] = '\0';
          *pybuftmp='\0';  // clear all the selected chars, reparse
      }
      else
      {
          inbuf[strlen(inbuf)-1] = '\0';
          if(inmd->flg_english) strcpy(inbuftmp,inbuf);
          else 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';
          inmd->flg_english = 1;
          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);

  tmpbuf[0]='\0';
  for (i = 0; i<inmd->lenpy; i++)
  {
       strcat(tmpbuf,inmd->pinyin[i]);
  }
  if (strcmp(inmd->inbuf,tmpbuf)) inmd->flg_english = 1;
  else inmd->flg_english = 0;
//  printf("%s,%s\n",inmd->inbuf,tmpbuf);

  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;
     default:  // select some keys
       if ( (ch>='1' && ch<='9') || ch=='0' || ch==' ' || ch=='\n')
           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;
      }
      if (pybuf[offset] == 'v' || pybuf[offset] == 'i' || pybuf[offset] == 'u')
      {
          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,ch2='\0';

  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]);
    if(tmplen >1)
	    ch2 = pinyin[i][1];
    for(j=0; (keytmp = pytab[ahead][j].key); j++)
    {
      // if ( tmplen == 1 || !strncmp(pinyin[i],pytab[ahead][j].py,tmplen))
      if ( tmplen == 1 || !strcmp(pinyin[i],pytab[ahead][j].py)
		      || ((tmplen==2) &&(!FuzzyPinyin)&&(ch=='z'||ch=='c'||ch=='s')
			      &&(ch2=='h')))
          // 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;
  }

  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 ();
    }
    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);
    if(p->flg_english) strcpy (buf, p->inbuf);
    else strcpy (buf, p->iapybuf);
    return 1;    
}

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


