/*
Copyright 1990-2003 Sun Microsystems, Inc. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions: The above copyright notice and this
permission notice shall be included in all copies or substantial
portions of the Software.


THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE OPEN GROUP OR SUN MICROSYSTEMS, INC. BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE EVEN IF
ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH DAMAGES.


Except as contained in this notice, the names of The Open Group and/or
Sun Microsystems, Inc. shall not be used in advertising or otherwise to
promote the sale, use or other dealings in this Software without prior
written authorization from The Open Group and/or Sun Microsystems,
Inc., as applicable.


X Window System is a trademark of The Open Group

OSF/1, OSF/Motif and Motif are registered trademarks, and OSF, the OSF
logo, LBX, X Window System, and Xinerama are trademarks of the Open
Group. All other trademarks and registered trademarks mentioned herein
are the property of their respective owners. No right, title or
interest in or to any trademark, service mark, logo or trade name of
Sun Microsystems, Inc. or its licensors is granted.

*/

#include  <stdio.h>
#include  <string.h>
#include  <stdlib.h>
#include  "GeneType.h"
#include  "PyStatic.h"
#include  "PyBig5.h"
#include  "Ciku.h"

VOID  DecompPeIntArray(JINT *pnOutPreedit, CHAR* szDspPreedit);
JINT  MakeOPE(JINT nHalfMatchLen, JINT nIUVetc, JINT nYJCode);
VOID  ParseRawInputStr(char* szPreedit, int* pnOutPreedit);
JINT  GetNextUnit(char* szPreedit, JINT nPos, char* szNextUnit);
JINT  MatchUnitByYinjie(char* szNextUnit);
JINT  GetFirstYinJie(int* pnYJArray);
 
/*
**  Decompose pnOutPreedit Array to a szString.
*/
VOID DecompPeIntArray(JINT *pnOutPreedit, CHAR* szDspPreedit)
{
	JINT    i, j, tmp;
	JINT    nHalfMatchLen, nIUVetc, nYJCode;
	CHAR    szHalfMatch[7];
	
	i = 0;
	tmp = 1;

	for (i = 0; i < 256; i++)
		szDspPreedit[i] = '\0';
		
	for (i = 0; pnOutPreedit[i] != 0; )
	{
		tmp = pnOutPreedit[i];
		nYJCode = tmp & 0x01FF;
		nIUVetc = (tmp >> 9) & 0x07;
		nHalfMatchLen = (tmp >> 12) & 0x0F;
		
		if (nIUVetc != 0)
		{
			if (nIUVetc == 4)
			{
				strcat (szDspPreedit, "\'");
				i++;
			}
			else
			{
				/*
				**  Add a space before [iuv] except that this is 
				**  the first CHAR in Preedit string or a ['ivu]
				**  ahead it.
				**  MakeOPE(0, 4, 0) is 0000 100 000000000 = 0x0800 [']
				**  MakeOPE(0, 1, 0) is 0000 001 000000000 = 0x0200 [i]
				**  MakeOPE(0, 2, 0) is 0000 010 000000000 = 0x0400 [u]
				**  MakeOPE(0, 3, 0) is 0000 011 000000000 = 0x0600 [v]
				*/
				if ( (i > 0) && (pnOutPreedit[i-1] != 0x0800)	\
				             && (pnOutPreedit[i-1] != 0x0200)	\
				             && (pnOutPreedit[i-1] != 0x0400)	\
				             && (pnOutPreedit[i-1] != 0x0600) )
					strcat (szDspPreedit, " ");
				
				if (nIUVetc == 1)
					strcat (szDspPreedit, "i");
				else  if (nIUVetc == 2)
					strcat (szDspPreedit, "u");
				else  if (nIUVetc == 3)
					strcat (szDspPreedit, "v");
				i++;
			}
		}
		else if ((nIUVetc == 0) && (nHalfMatchLen == 6))
		{
			/*
			** A valid Yinjie code. Add a SPACE before
			** Yinjie string expect that there is a ['] ahead or it is the first.
			** MakeOPE(0, 4, 0) is 0000 100 000000000 = 0x0800
			*/
			if ((i > 0) && (pnOutPreedit[i-1] != 0x0800))
				strcat (szDspPreedit, " ");
				
			if (nYJCode >= 450)
				strcat (szDspPreedit, SHENGMUSTR[nYJCode - 450]);
			else if (nYJCode < 450)
				strcat (szDspPreedit, YINJIESTR_CSZ[nYJCode]);
				
			i++;
		}
		else if ((nHalfMatchLen > 0) && (nHalfMatchLen < 6) && (nIUVetc == 0))
		{
			for (j = 0; j < nHalfMatchLen; j++)
				szHalfMatch[j] = (char)pnOutPreedit[i + j + 1];
			szHalfMatch[j] = '\0';
			if ((i > 0) && (pnOutPreedit[i-1] != 0x0800))
				strcat (szDspPreedit, " ");
			strcat (szDspPreedit, szHalfMatch);
			i += (j + 1);
		}
		else
		{
			i++;
			fprintf (stderr, "Error in DecompPeIntArray()\n");
		}
	}
}


JINT MakeOPE(JINT nHalfMatchLen, JINT nIUVetc, JINT nYJCode)
{
	return (nYJCode + (nIUVetc << 9) + (nHalfMatchLen << 12));
}


/*
**  szPreedit[] contains only [a]~[z] and [']. No other chars included.
**  pnOutPreedit points to a array that contains the parsed result in the
**  following format:
**      |  4Bits   |  3Bits   |       9Bits        |
**      ===========++++++++++++=====================
**      4Bits: nHalfMatchLen ==> Indicates that the following integer is
**             HalfMatched Pinyin string. 0x61[a] ~ 0x7A[z].
**             Min is 1, Max is 5.  If it is 7, indicates 9Bits is YJCode.
**      3Bits: 000 ==> Default
**             001 ==> [i]
**             010 ==> [u]
**             011 ==> [v]
**             100 ==> [']
**      9Bits: 0 ~ 511  Yinjie Code or Shengmu code
**             Same as notes before function MatchUnitByYinjie().
*/

VOID ParseRawInputStr(char* szPreedit, int* pnOutPreedit)
{
	CHAR    szNextUnit[7];
	JINT    nPELen;
	JINT    nCurOff, nOPEOff, nRef, nPy;
	JINT    nMatchFlag,  nMatchSMLen,  nMatchYMLenFit,  nMatchYMLenMax,  nMatchYinjie;
	JINT    nMatchFlag2, nMatchSMLen2, nMatchYMLenFit2, nMatchYMLenMax2, nMatchYinjie2;
	JINT    i, j, tmp, tmp1, tmp2;
	CHAR    cEnd, cNext;
	
	JINT    nHalfMatchLen, nIUVetc, nYJCode;

	nHalfMatchLen = nIUVetc = nYJCode = 0;
	nCurOff = 0;
	nRef = nPy = 0;
	nOPEOff = 0;
	
	nPELen = (int)strlen(szPreedit);
	
	while (nPELen > nCurOff)
	{
		for (i = 0; i < 7; i++)
			szNextUnit[i] = '\0';
		
		tmp = GetNextUnit(szPreedit, nCurOff, szNextUnit);
		nRef = (tmp >> 8) & 0x00FF;
		nPy = tmp & 0x00FF;

		/* a ['] is the front of szPreedit[nCurOff] */
		if (nRef == 1)
		{
			pnOutPreedit[nOPEOff] = MakeOPE (0, 4, 0);
			nOPEOff ++;
			nCurOff ++;
		}
		
		if (nPy == 0)
			nCurOff = nPELen;	/* break;  OR: nCurOff = nPELen; */
		else				/* nPy > 0 */
		{
			tmp = MatchUnitByYinjie (szNextUnit);
			nMatchYMLenFit = (tmp >> 16) & 0x07;
			nMatchFlag = (tmp >> 13) & 0x07;
			nMatchSMLen = (tmp >> 12) & 0x01;
			nMatchYMLenMax = (tmp >> 9) & 0x07;
			nMatchYinjie = tmp & 0x01FF;
			
			tmp1 = nCurOff + nMatchSMLen + nMatchYMLenMax + 1;
			
			if (nMatchFlag == 1)
			{
				/* The first Char is [i][u][v] */
				if (szNextUnit[0] == 'i')
					tmp = 1;
				else if (szNextUnit[0] == 'u')
					tmp = 2;
				else if (szNextUnit[0] == 'v')
					tmp = 3;
					
				pnOutPreedit[nOPEOff] = MakeOPE(0, tmp, 0);
				nOPEOff ++;
				nCurOff ++;
			}
			/*
			**   A Bug Fixed: ORG: zhonw => zhon w // NEW: zhonw => zh o n w
			**   97-10-5
			*/
			else if ( (nMatchFlag == 2) && (tmp1 == nPELen) )
			{
				nHalfMatchLen = nMatchSMLen + 1 + nMatchYMLenMax;
				pnOutPreedit[nOPEOff] = MakeOPE (nHalfMatchLen, 0, 0);
				nOPEOff ++;
				for (j = 0; j < (nMatchSMLen + nMatchYMLenMax + 1); j++ )
				{
					pnOutPreedit[nOPEOff] = (int)szNextUnit[j];
					nOPEOff ++;
					nCurOff ++;
				}
			}
			else if (nMatchFlag == 0)
			{
				cEnd = szPreedit[nCurOff + nMatchSMLen + nMatchYMLenFit];
				cNext = szPreedit[nCurOff + nMatchSMLen + 1 + nMatchYMLenFit];
				
				/*
				** G[aeou]  //  N[aeiouv]  //  R[aeiou]
				*/
				if( ((cEnd == 'g') && ((cNext == 'a') || (cNext == 'e') || 	\
						       (cNext == 'o') || (cNext == 'u') )) 	\
				 || ((cEnd == 'n') && ((cNext == 'a') || (cNext == 'e') || 	\
				                       (cNext == 'i') || (cNext == 'o') || 	\
				                       (cNext == 'u') || (cNext == 'v') ))	\
				 || ((cEnd == 'r') && ((cNext == 'a') || (cNext == 'e') || 	\
				                       (cNext == 'i') || (cNext == 'o') || 	\
				                       (cNext == 'u') )) )
				{
					for (i = 0; i < (nMatchSMLen + nMatchYMLenFit); i++)
						szNextUnit[i] = szPreedit[nCurOff + i];
					for (i = (nMatchSMLen + nMatchYMLenFit); i < 7; i++)
						szNextUnit[i] = '\0';
					
					tmp2 = MatchUnitByYinjie (szNextUnit);
					nMatchYMLenFit2 = (tmp2 >> 16) & 0x07;
					nMatchFlag2 = (tmp2 >> 13) & 0x07;
					nMatchSMLen2 = (tmp2 >> 12) & 0x01;
					nMatchYMLenMax2 = (tmp2 >> 9) & 0x07;
					nMatchYinjie2 = tmp2 & 0x01FF;
					
					/*
					** The following condition may be adjusted to get more
					** precision and general. Thus, following syntax is equal.
					** eran <==> er'an  ana <==> an'a  IS IT BEST????
					*/
					if (nMatchFlag2 == 0)
					{
						nMatchYinjie = nMatchYinjie2;
						nMatchSMLen = nMatchSMLen2;
						nMatchYMLenFit = nMatchYMLenFit2;
					}
				}
				
				pnOutPreedit[nOPEOff] = MakeOPE (6, 0, nMatchYinjie);
				nOPEOff ++;
				nCurOff += (nMatchSMLen + 1 + nMatchYMLenFit);
			}
			else
			{
				/* nMatchFlag = 3, or 2 in middle */
				/* Setting nHalfMatchLen to 6 to avoid MakeOPE(0, 0, 0) */
				
				pnOutPreedit[nOPEOff] = MakeOPE (6, 0, nMatchYinjie);
				nOPEOff ++;
				nCurOff += (nMatchSMLen + 1 + nMatchYMLenFit);
			}
		}
			
	} /* End of While() */
	pnOutPreedit[nOPEOff] = 0;
}


/*
**  szPreedit[] consisted of [a]~[z] ['] and [\0]
**  nOff indicates the start offset to get next unit
**  szNextUnit points to the buffer to store next unit
**  The return JINT indicates the chars have been read from szPreedit.
**                  |  8Bits  |  8Bits  |
**                  ==========-----------
**                  =: 00000001  ==> a ['] in the front of szPreedit[nOff]
**                  -: 00000xxx  ==> 0-1-2-3-4-5-6 Length of this unit[a]~[z]
*/

JINT GetNextUnit(char* szPreedit, JINT nOff, char* szNextUnit)
{
	JINT    nRef, nPy;
	JINT    i;

	nRef = nPy = 0;
	i = nOff;
	
	/* Skip ['] at position szPreedit[nOff] */
	if (szPreedit[i] == '\'')
	{
		nRef++;
		i++;
	}
		
	while( (szPreedit[i] != '\0') && (szPreedit[i] != '\'') && (nPy <= 6) )
	{
		szNextUnit[nPy] = szPreedit[i];
		i++;
		nPy++;
	}
	if (nPy == 7)
		nPy = 6;
	
	return ((nRef << 8) + nPy);
}


/*
**  Lookup the SHENGMUSTR[] and YINJIESTR_CSZ[] to determine the max longest YINJIE
**  which match szNextUnit from header. return the value which indicate
**  the YINJIE. 
**  RETURN JINT     Range 0~414:   YINJIESTR_CSZ[return]
**                  Range > 450:   SHENGMUSTR[return - 450]
**                        Notes:   return-450 don't contains ['a' 'e' 'm' 'n' 'o']
**
**                  |<= High ......................  Low =>|
**                  |  3Bits  |  1Bit  |  3Bits  |  9Bits  |
**                   0 - 8   9Bits:  0~414  & 450 + Shengmu
**                                   0x01FF indicates invalid.
**                   9 - 11  3Bits:  1-2-3-4  Length of Matched Yunmu chars
**                  12       1Bit:   0-1 Length of Shengmu, 1 => [ch][sh][zh]  
**                  13 - 15  4Bits:  000  ==> Full Matched
**                                   001  ==> Invalid Char [i][u][v]
**                                   010  ==> Half Matched. ex, "zho".  Length is 3
**                                   011  ==> Match ShengMu only
**
**  Question:       How about if the first CHAR in szNextUnit is [i][u][v]?
**                  How to convert [u] and [v] automatically?
**                  How to process half match question? for example, "zho"?
*/

/*
**                  How to make this function match to mode "mohu pinyin"?
**                              include:  l/n, n/ng/, f/h,  zh ch sh/ z c s
**                  NO, MOHU is only considered during "YinjieToHzstr".
*/
JINT MatchUnitByYinjie(char* szNextUnit)
{
	JINT   i, j;
	CHAR   cFirstCh;
	JINT   nShengmuLen, nFromYJStr, nToYJStr, nShengmuIndex;	
	JINT   nMatchYinjie, nMatchYMLenFit, nMatchSMLen, nMatchFlag, nMatchYMLenMax;
	JINT   tmp;
	
	cFirstCh = szNextUnit[0];
	
	nMatchYinjie = nMatchYMLenFit = nMatchYMLenMax = nMatchSMLen = nMatchFlag = 0;
	
	if ((cFirstCh == 'i') || (cFirstCh == 'u') || (cFirstCh == 'v'))
	{
		nMatchSMLen = 0;	/* Indicates 1 */
		nMatchYMLenFit = 0;
		nMatchYMLenMax = 0;
		nMatchFlag = 1;         /* Invalid Char [i][u][v] */
		nMatchYinjie = 0x1FF;   /* Invalid Shenmu */
	}
	else if ((cFirstCh >= 'a') && (cFirstCh <= 'z'))
	{
		nMatchSMLen = 0;
		nMatchYMLenMax = 0;
		nMatchYMLenFit = 0;
		
		if ((cFirstCh == 'c') && (szNextUnit[1] == 'h'))
		{
			nFromYJStr = INDEXSMTOYINJIE[3];
			nToYJStr = INDEXSMTOYINJIE[4];
			nMatchSMLen = 1;
			nShengmuIndex = 3;	/* [ch], same as INDEXMAGIC[(int)('i' - 'a')] */
		}
		else if ((cFirstCh == 's') && (szNextUnit[1] == 'h'))
		{
			nFromYJStr = INDEXSMTOYINJIE[19];
			nToYJStr = INDEXSMTOYINJIE[20];
			nMatchSMLen = 1;
			nShengmuIndex = 19;	/* [sh], same as INDEXMAGIC[(int)('u' - 'a')] */
		}
		else if ((cFirstCh == 'z') && (szNextUnit[1] == 'h'))
		{
			nFromYJStr = 395; /*INDEXSMTOYINJIE[25]; */
			nToYJStr = 415; /*INDEXSMTOYINJIE[26]; */
			nMatchSMLen = 1;
			nShengmuIndex = 25;	/* [zh], same as INDEXMAGIC[(int)('v' - 'a')] */
		}
		else
		{
			nFromYJStr = INDEXSMTOYINJIE[ INDEXMAGIC[(int)(cFirstCh - 'a')] ];
			nToYJStr = INDEXSMTOYINJIE[ INDEXMAGIC[(int)(cFirstCh - 'a')] + 1];
			nMatchSMLen = 0;
			nShengmuIndex = INDEXMAGIC[(int)(cFirstCh - 'a')];
		}
		
		nShengmuLen = nMatchSMLen + 1;

		nMatchYinjie = 450 + nShengmuIndex;
		nMatchFlag = 3;				/* Match ShengMu Only */

		for (i = nFromYJStr; i < nToYJStr; i++)
		{
			for (j = nShengmuLen; ((szNextUnit[j] == YINJIESTR_CSZ[i][j]) && (YINJIESTR_CSZ[i][j] != '\0')); )
			{
				j += 1;
			/* Null Loop Here. It is Right */
			}
			
			/*
			** [cu] => [cuan],  [su] => [suan],  [zu] => [zuan] ??
			** Get the Max Matchment Length among YINJIESTR_CSZ[i],
			** where, [i] is between nFromYJStr and nToYJStr.
			** Example, [su] and [suan] is both valid Yinjie, but [sua] is invalid.
			** How to process if user input only [sua]?
			*/
			/*
			** !!!!NOTE!!!! The following condition cannot be changed to
			** !!!!NOTE!!!! ((j - nShengmuLen) >= nMatchYMLenMax)  ...... 
			*/
			if ( ((j - nShengmuLen) > nMatchYMLenMax) && ((j - nShengmuLen) > 0) )
			{
				nMatchYMLenMax = j - nShengmuLen;
				if (YINJIESTR_CSZ[i][j] != '\0')
				{
					nMatchFlag = 2;		/* Half Matchment. [cua] */
				}
				else
				{
					nMatchYinjie = i;
					nMatchFlag = 0;		/* Full Matchment */
					nMatchYMLenFit = nMatchYMLenMax;
				}
			}
		}
	}
	else
	{
		nMatchYinjie = nMatchYMLenFit = nMatchYMLenMax = nMatchSMLen = nMatchFlag = 0;
	}

	tmp = nMatchYinjie + (nMatchYMLenMax << 9) + (nMatchSMLen << 12) + (nMatchFlag << 13) + (nMatchYMLenFit << 16);
	
	return (tmp);
}


/*
**  Return YinJie Code if the first is a valid YinJie. Else return -1
**  Valid YinJie Code is [0 ~ 414,  and [450 ~ 475]
**  If there is a ['] before the next valid Yinjie, (0x00010000 + nYJCode) is returned
*/
JINT GetFirstYinJie(int* pnYJArray)
{
	JINT    nHalfMatchLen, nIUVetc, nYJCode;
	JINT    nTmp, nTmp1;
	
	nTmp = pnYJArray[0];
	nTmp1 = pnYJArray[1];
	
	if (nTmp != 0)
	{
		nYJCode = nTmp & 0x01FF;
		nIUVetc = (nTmp >> 9) & 0x07;
		nHalfMatchLen = (nTmp >> 12) & 0x0F;
		
		if ((nIUVetc == 0) && (nHalfMatchLen == 6))
			return nYJCode;
		else if (nIUVetc == 4)		/* ['] */
		{
			nYJCode = nTmp1 & 0x01FF;
			nIUVetc = (nTmp1 >> 9) & 0x07;
			nHalfMatchLen = (nTmp1 >> 12) & 0x0F;
			
			if ((nIUVetc == 0) && (nHalfMatchLen == 6))
				return (nYJCode + 0x00010000);
			else
				return -1;
		}
		else
			return -1;
	}
	else
		return -1;
}


