
/* 	
			UNICON PROJECT

   		    Copyright (C) 1999 TLDN
	
   Authors:	Li Qi Chen <chrisl@turbolinux.com.cn>
		Arthur Ma  <arthur.ma@turbolinux.com.cn>
		Justin Yu  <justiny@turbolinux.com.cn>

   Homepage:    http://turbolinux.com.cn/TLDN/chinese/project/unicon/index.html
   Mailinglist: unicon@turbolinux.com.cn 	
   Download:    http://turbolinux.com.cn/TLDN/chinese/project/unicon/download.html

   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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

/* Imm Standard Interfaces */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <ImmModule.h>
#include "xl_hzinput.h"
//#include "HL_Debug.h"

#define CODING_SINGLE_BYTE	0x01
#define CODING_DOUBLE_BYTES	0x02
#define CODING_FOUR_BYTES	0x04


hz_input_table *IntCode_Init(void)
{
	hz_input_table *table;
	int i,index,cnt;

	table = malloc(sizeof(hz_input_table));

	if (table == NULL)
		return NULL;

	strcpy(table->magic_number,MAGIC_NUMBER);
	strcpy(table->ename, "IntCode");
	strcpy(table->cname, "¡¾ÄÚÂë¡¿");
	strcpy(table->selkey, "0123456789");

        //printf("%s", table->selkey);

	table->last_full = 1;
	for(i = 0; i < 128; i++)
	{
		table->KeyMap[i] = 0;
		if ((i >= '0' && i <= '9') || (i >= 'a' && i <= 'f'))
		{
			if (i >= '0' && i <= '9')
				index = i - '0';
			else index = i -'a' + 10;
			
			table->KeyMap[i] = index;
			table->KeyName[index] = toupper(i);
		}
               //printf("%d---%d,",i,table->KeyMap[i]);
	}    
	// The following dump the KeyMap and KeyName
#if 0
	fprintf (stderr, "The following is the table->KeyMap.\n");
	for (cnt=0;cnt<128;cnt++)
	{
		for (;(cnt+1)%16;cnt++)
			fprintf (stderr, "%X ", table->KeyMap[cnt]);
		fprintf (stderr, "%X\n", table->KeyMap[cnt]);
	}
	fprintf (stderr, "The following is the table->KeyName.\n");
	for (cnt=0;cnt<64;cnt++)
	{
		for (;(cnt+1)%16;cnt++)
			fprintf (stderr, "%X ", table->KeyName[cnt]);
		fprintf (stderr, "%X\n", table->KeyName[cnt]);
	}
#endif
	return table;
}

int ConfigureInputArea (HzInputTable_T * pHzInputTable, int nSelectionLen)
{
	pHzInputTable->MaxSelectLen = nSelectionLen;
	return 1;
}

void ResetInput(HzInputTable_T * pHzInputTable)
{
	bzero (pHzInputTable->InpKey, sizeof(pHzInputTable->InpKey));
	bzero (pHzInputTable->seltab, sizeof(pHzInputTable->seltab));

	pHzInputTable->MultiPageMode = 0;
	pHzInputTable->NextPageIndex = 0;
	pHzInputTable->CurrentPageIndex = 0;
	pHzInputTable->CurSelNum = 0;
	pHzInputTable->InputCount = 0;
	pHzInputTable->InputMatch = 0;
	pHzInputTable->val1 = 0;
}

void ClearSelection(HzInputTable_T * pHzInputTable)
{
	pHzInputTable->MultiPageMode = 0;
	pHzInputTable->NextPageIndex = 0;
	pHzInputTable->CurrentPageIndex = 0;
	pHzInputTable->CurSelNum = 0;
}

void IntCode_FindMatchKey(HzInputTable_T *pHzInputTable)
{
	unsigned long Key;

	if (pHzInputTable->val1 == CODING_FOUR_BYTES)
        	Key = (pHzInputTable->InpKey[0] << 28) 
		      | (pHzInputTable->InpKey[1] << 24) 
		      | (pHzInputTable->InpKey[2] << 20) 
		      | (pHzInputTable->InpKey[3] << 16) 
		      | (pHzInputTable->InpKey[4] << 12) 
		      | (pHzInputTable->InpKey[5] << 8);
	else 
		Key = (pHzInputTable->InpKey[0] << 12) 
		      | (pHzInputTable->InpKey[1] << 8);
	
	switch(pHzInputTable->InputCount)
	{
		case 3:
			if (pHzInputTable->val1 == CODING_DOUBLE_BYTES)
			{
				pHzInputTable->StartKey = Key + (pHzInputTable->InpKey[2] << 4);
				pHzInputTable->EndKey = pHzInputTable->StartKey + 0x0F; 
			}
			else
				pHzInputTable->StartKey = pHzInputTable->EndKey = 0;    /* not display selection */
            		break;
			
		case 6:
			pHzInputTable->StartKey = Key + 0x30;
			pHzInputTable->EndKey = Key + 0x39;
			break;
			
		default:
			pHzInputTable->StartKey = pHzInputTable->EndKey = 0;
	}
}


void IntCode_FillMatchChars(HzInputTable_T *pHzInputTable, int index)
{
    int MaxSel = 10, i;
   
    int CurSelNum = 0;
    if (pHzInputTable->InputCount < 2) return;
    if (pHzInputTable->InputCount == 6)
    {
        while( CurSelNum < MaxSel && index <= pHzInputTable->EndKey)
		{
            pHzInputTable->seltab[CurSelNum][0] = (char)((index & 0xFF000000) >> 24); 
            pHzInputTable->seltab[CurSelNum][1] = (char)((index & 0x00FF0000) >> 16);
            pHzInputTable->seltab[CurSelNum][2] = (char)((index & 0x0000FF00) >> 8);
		    pHzInputTable->seltab[CurSelNum][3] = (char)((index & 0x000000FF));
		    pHzInputTable->seltab[CurSelNum][4] = '\0';
         	CurSelNum++;
         	index++;
    	}
    
    }
    else
    {
        while( CurSelNum < MaxSel && index <= pHzInputTable->EndKey)
		{
            pHzInputTable->seltab[CurSelNum][0] = index / 256; 
        	pHzInputTable->seltab[CurSelNum][1] = index % 256;
         	pHzInputTable->seltab[CurSelNum][2] = '\0';
         	CurSelNum++;
         	index++;
		}
    }
    
	pHzInputTable->CurSelNum = CurSelNum;
	for(i = CurSelNum; i < 16; i++)
       pHzInputTable->seltab[i][0] = '\0';  /* zero out the unused area */

    pHzInputTable->InputMatch = pHzInputTable->InputCount;

    if ( index <= pHzInputTable->EndKey && CurSelNum == MaxSel && MaxSel == 10)
    {
      /* has another matched key, so enter MultiPageMode, has more pages */
        pHzInputTable->NextPageIndex = index;
        pHzInputTable->MultiPageMode = 1;
    }  
    else if (pHzInputTable->MultiPageMode)
    {
        pHzInputTable->NextPageIndex = pHzInputTable->StartKey; /* rotate selection */
    }
    else pHzInputTable->MultiPageMode = 0;
}

int Intcode_HZFilter(HzInputTable_T *pHzInputTable, unsigned char key, char *buff, int *len)
{
	int inkey = 0;
	char *pSelChar = NULL;
	int nSelIndex;

 
        // printf ("In Intcode_HZFilter................\n");

	switch ( key )
	{
		case '\010':  /* BackSpace Ctrl+H */
		case '\177':  /* BackSpace */
			if (pHzInputTable->InputCount > 0) 
			{
				pHzInputTable->InpKey[--pHzInputTable->InputCount]=0;

				if (pHzInputTable->InputCount == 2)
				{
					pHzInputTable->val1 = 0;
					ClearSelection(pHzInputTable);
				}	

				if (pHzInputTable->InputCount == 0)
					ResetInput(pHzInputTable);
				else if (pHzInputTable->InputCount > 2)
				{
					IntCode_FindMatchKey(pHzInputTable);
					pHzInputTable->MultiPageMode = 0;
					pHzInputTable->CurrentPageIndex = pHzInputTable->StartKey;
					IntCode_FillMatchChars(pHzInputTable, pHzInputTable->StartKey); 
				}
			}
			else return 0;
			break;

		case '\033':  /* ESCAPE */
			if (pHzInputTable->InputCount > 0) 
				ResetInput(pHzInputTable);
			else return 0;
			break;

		case '-':
		case ',':
			if ( pHzInputTable->MultiPageMode )
			{
			   if ( pHzInputTable->CurrentPageIndex > pHzInputTable->StartKey) 
					pHzInputTable->CurrentPageIndex = pHzInputTable->CurrentPageIndex - 10;
			   else pHzInputTable->CurrentPageIndex = pHzInputTable->StartKey;
			   
			   IntCode_FillMatchChars(pHzInputTable, pHzInputTable->CurrentPageIndex);
			}
			else return 0;
			break;

		case '.':
		case '=':
			if ( pHzInputTable->MultiPageMode )
			{
			   pHzInputTable->CurrentPageIndex = pHzInputTable->NextPageIndex;
			   IntCode_FillMatchChars(pHzInputTable, pHzInputTable->CurrentPageIndex);
			}
			else return 0;
			break;

		case ' ':
			if (pHzInputTable->CurSelNum == 0 )
			   return 0;
			if ( pHzInputTable->seltab[0][0] )
			{
				strcpy(buff, pHzInputTable->seltab[0]);
				*len = strlen(buff);
				ResetInput(pHzInputTable);
				return 2;
			}   
			break;

		default:
			inkey = pHzInputTable->cur_table->KeyMap[key];
			pSelChar = strchr(pHzInputTable->cur_table->selkey, key);
			nSelIndex = pSelChar - pHzInputTable->cur_table->selkey;
			
			if (pSelChar 
			    && pHzInputTable->CurSelNum > 0 
			    && pHzInputTable->seltab[nSelIndex][0])
			{
				strcpy(buff, pHzInputTable->seltab[nSelIndex]);
				*len = strlen(buff);
				ResetInput(pHzInputTable);
				return 2;
			}
			//printf("nSelIndex = %d inkey = %d, InputCount = %d, key = %c, Coding = %ld\n", nSelIndex, inkey,pHzInputTable->InputCount, key, pHzInputTable->val1);
			switch(pHzInputTable->InputCount % 4)
			{
				case 0:  /* 8-F */
					if (inkey < 8)
					{
						//pHzInputTable->val1 = CODING_SINGLE_BYTE;
						return 0;	
					}	
					else 
					{
						if ((pHzInputTable->val1 & CODING_FOUR_BYTES) && 
                                                    (pHzInputTable->InputCount == 4) &&
	                                            (pHzInputTable->InpKey[pHzInputTable->InputCount - 4] == 8) &&
	                                            (pHzInputTable->InpKey[pHzInputTable->InputCount - 3] == 4) &&
	                                            (pHzInputTable->InpKey[pHzInputTable->InputCount - 2] == 3) &&
	                                            (pHzInputTable->InpKey[pHzInputTable->InputCount - 1] == 1) &&
					            (inkey > 9))
						{
                                                        return 0;                                 
                                                } else {
						    pHzInputTable->InpKey[pHzInputTable->InputCount++] = inkey;
                                                }
					}	
					break;
				
				case 1:  /* 0-F */
					if (((pHzInputTable->InpKey[pHzInputTable->InputCount - 1] == 8) && (inkey == 0)) || 
					    ((pHzInputTable->InpKey[pHzInputTable->InputCount - 1] == 15) && (inkey == 15))) //0x08 or 0xFF is invalid
					{
						return 0;
					}
					else
					{
						pHzInputTable->InpKey[pHzInputTable->InputCount++] = inkey;
						
						if (pHzInputTable->InputCount == 6)
						{
							IntCode_FindMatchKey(pHzInputTable);
							pHzInputTable->MultiPageMode = 0;
							pHzInputTable->CurrentPageIndex = pHzInputTable->StartKey;
							IntCode_FillMatchChars(pHzInputTable, pHzInputTable->StartKey);
							pHzInputTable->InpKey[pHzInputTable->InputCount++] = 3;
						}
					}
					break;

				case 2:  /* A-F */
					if (inkey < 3)
					{
						return 0;
					}


					if (inkey == 3)
					{
                                            if (pHzInputTable->InputCount < 4) {
	                                        if ((pHzInputTable->InpKey[pHzInputTable->InputCount - 2] == 8) && 
	                                            (pHzInputTable->InpKey[pHzInputTable->InputCount - 1] <= 4) && 
	                                            (pHzInputTable->InpKey[pHzInputTable->InputCount - 1] >= 1)) {
						        pHzInputTable->val1 = CODING_FOUR_BYTES;
						        ClearSelection(pHzInputTable);
						        pHzInputTable->InpKey[pHzInputTable->InputCount++] = inkey;
                                                    } else {
                                                        return 0;
                                                    }
                                            } else {
					        pHzInputTable->val1 = CODING_FOUR_BYTES;
					        ClearSelection(pHzInputTable);
					        pHzInputTable->InpKey[pHzInputTable->InputCount++] = inkey;
                                            }
					}
					else
					{
						//if (pHzInputTable->val1)
						//{
						//	return 0;
						//}
						//else
						{
							pHzInputTable->val1 = CODING_DOUBLE_BYTES;
							pHzInputTable->InpKey[pHzInputTable->InputCount++] = inkey;
							IntCode_FindMatchKey(pHzInputTable);
							pHzInputTable->MultiPageMode = 0;
							pHzInputTable->CurrentPageIndex = pHzInputTable->StartKey;
							IntCode_FillMatchChars(pHzInputTable, pHzInputTable->StartKey);
						}	
					}
					break;
				case 3:   /* output char */
						  /* A1A1-FEFE */
					if (((pHzInputTable->InpKey[pHzInputTable->InputCount - 1] == 7) && (inkey == 15)) || 
					    ((pHzInputTable->InpKey[pHzInputTable->InputCount - 1] == 15) && (inkey == 15)) ||
					    ((pHzInputTable->InpKey[pHzInputTable->InputCount - 1] == 3) && (inkey > 9)))
					//0x7F or 0xFF or 3A-3F is invalid
					{
						return 0;
					}
					else
					{
						if (pHzInputTable->val1 & CODING_DOUBLE_BYTES)
						{
							pHzInputTable->seltab[0][0] = (pHzInputTable->InpKey[0] << 4) | pHzInputTable->InpKey[1];
							pHzInputTable->seltab[0][1] = (pHzInputTable->InpKey[2] << 4) | inkey ;
							pHzInputTable->seltab[0][2] = '\0';
							//DebugLog("double bytes %s\n", seltab[0]);
							strcpy(buff, pHzInputTable->seltab[0]);
							*len = strlen(buff);
							ResetInput(pHzInputTable);
							return 2;
							
						}
						if ((pHzInputTable->val1 & CODING_FOUR_BYTES) && 
                                                    (pHzInputTable->InputCount == 3) &&
	                                            (pHzInputTable->InpKey[pHzInputTable->InputCount - 2] == 4) &&
					            (inkey > 1))
						{
                                                        return 0;                                 
                                                }
						if ((pHzInputTable->val1 & CODING_FOUR_BYTES) && (pHzInputTable->InputCount == 7))
						{
							pHzInputTable->seltab[0][0] = (pHzInputTable->InpKey[0] << 4) | pHzInputTable->InpKey[1];
							pHzInputTable->seltab[0][1] = (pHzInputTable->InpKey[2] << 4) | pHzInputTable->InpKey[3];
							pHzInputTable->seltab[0][2] = (pHzInputTable->InpKey[4] << 4) | pHzInputTable->InpKey[5];
							pHzInputTable->seltab[0][3] = (pHzInputTable->InpKey[6] << 4) | inkey;
							pHzInputTable->seltab[0][4] = '\0';
							//write(tty_fd, seltab[0], 4);
							strcpy(buff, pHzInputTable->seltab[0]);
							*len = strlen(buff);
							ResetInput(pHzInputTable);
							return 2;
						}
                
       					pHzInputTable->InpKey[pHzInputTable->InputCount++] = inkey;
					}
					break;
			}
	} /* switch */
	return 1;
}

int GetSelectDisplay (HzInputTable_T * pHzInputTable, char *strBuf, int nBufLen)
{
	int i, pos = 0,len;

	
	if (pHzInputTable->MultiPageMode && 
	    pHzInputTable->CurrentPageIndex != pHzInputTable->StartKey) /* not first page */
	{
		strcpy(strBuf + pos, "< ");    
		pos += 2;
	}

	for( i = 0; i < pHzInputTable->CurSelNum; i++ )
	{
		if (!pHzInputTable->seltab[i][0])
			break;

		len = strlen(pHzInputTable->seltab[i]);
		if (pos + len + 3 > nBufLen) break;
		sprintf(strBuf + pos, "%d%s ", i, pHzInputTable->seltab[i]);
		pos += len + 2; 

		//printf("<%d> %#lx\n",i, *(long*)pHzInputTable->seltab[i]);

	}

	if ( pHzInputTable->MultiPageMode && 
	     pHzInputTable->NextPageIndex != pHzInputTable->StartKey)  /* not last page */
	{
		strcpy(strBuf + pos, "> ");
		pos += 2;
	}
	strBuf[pos] = '\0';
	return pHzInputTable->CurSelNum ? 1 : 0;
}

int GetInputDisplay (HzInputTable_T * pHzInputTable, char *buf)
{
	int i;

	for( i = 0; i < pHzInputTable->InputCount ; i++)
		sprintf(buf + i, "%c", pHzInputTable->cur_table->KeyName[pHzInputTable->InpKey[i]]);
	buf[pHzInputTable->InputCount] = '\0';	
	return pHzInputTable->InputCount ? 1: 0; 
}


/* ********************** Now the interface functions *********************** */
static IMM_CLIENT *IMM_open (char *szFileName, long type)
{
    HzInputTable_T *pHzInputTable_T;
    IMM_CLIENT *pIMM_CLIENT;
    hz_input_table *phz_input_table;

    phz_input_table = IntCode_Init();
    if (phz_input_table == NULL)
        return NULL;
    
    pHzInputTable_T = (HzInputTable_T *) malloc (sizeof (HzInputTable_T));
    if (pHzInputTable_T == NULL)
        return NULL;

    pIMM_CLIENT = (IMM_CLIENT *) malloc (sizeof (IMM_CLIENT));
    if (pIMM_CLIENT == NULL)
    {
        free (pHzInputTable_T);
        free (phz_input_table);
        return NULL;
    };
	ResetInput(pHzInputTable_T);
	ConfigureInputArea(pHzInputTable_T, 48);	
   // SetPhraseBuffer (&pIMM_CLIENT->m, pIMM_CLIENT->buf, sizeof (pIMM_CLIENT->buf));
    pIMM_CLIENT->pImmClientData = (void *) pHzInputTable_T;
    pHzInputTable_T->cur_table = phz_input_table;
    return pIMM_CLIENT;
}

static int IMM_save (IMM_CLIENT *p, char *szFileName)
{
    return 1;
}

static int IMM_close (IMM_CLIENT *p)
{
    HzInputTable_T *pClient = (HzInputTable_T *) p->pImmClientData; 
    free (pClient->cur_table);
    free (p->pImmClientData);
    free (p);
    return 1;
}

/* Indepent Modules support */
static int IMM_KeyFilter (IMM_CLIENT *p, u_char key, char *buf, int *len)
{
	buf[0] = '\0';
	*len = 0;
     	return Intcode_HZFilter ((HzInputTable_T *) p->pImmClientData, key, buf, len);
}

/* Input Area Configuration & Operation */
static int IMM_ConfigInputArea (IMM_CLIENT *p, int SelectionLen)
{
    return ConfigureInputArea (
           (HzInputTable_T *) p->pImmClientData, SelectionLen);
}

static int IMM_GetInputDisplay (IMM_CLIENT *p, char *buf, long buflen)
{
    return GetInputDisplay((HzInputTable_T *) p->pImmClientData, buf);
}

static int IMM_GetSelectDisplay (IMM_CLIENT *p, char *buf, long buflen)
{
    return GetSelectDisplay((HzInputTable_T *) p->pImmClientData, buf, buflen);
}

int IMM_ResetInput (IMM_CLIENT *p)
{
    //char buf[32];
    //int len;
    /* Send Esc */
    ResetInput ((HzInputTable_T *) p->pImmClientData);
    return 1;
}

PhraseItem * IMM_pGetItem (IMM_CLIENT *p, u_long n)
{
	return NULL;
}

/* Phrase Operation */
static int IMM_AddPhrase (IMM_CLIENT *pClient, PhraseItem *p)
{
    return 1;
}

static int IMM_ModifyPhraseItem (IMM_CLIENT *p, long n, PhraseItem *pItem)
{
    return 1;
}

static int IMM_Flush ()
{
    return 1;
}

#ifdef  __cplusplus
extern "C" 
#endif
struct ImmOperation ImmOp_Ptr = {
    "ÄÚÂë", 
    "HCON Chinese Input Version 3.0",
    "Author:Dianzhi Wang",
    IMM_CCE | IMM_LC_GB2312 << 24,
    IMM_open,
    IMM_save,
    IMM_close,

    /* Indepent Modules support */
    IMM_KeyFilter,
    IMM_ResetInput,

    /* Input Area Configuration & Operation */
    IMM_ConfigInputArea,
    IMM_GetInputDisplay,
    IMM_GetSelectDisplay,
    IMM_pGetItem,
    IMM_AddPhrase,
    IMM_ModifyPhraseItem,
    IMM_Flush,
};

#ifdef __CCE_HZINPUT_DEBUG__
int test (long a, PhraseItem *p)
{
    printf ("%s\n", p->szPhrase);
    return 1;
}

/*
    gcc -g -I../include -I. -D__CCE_HZINPUT_DEBUG__ xl_hzinput.c CCE_hzinput.c
*/

int main ()
{
    IMM_CLIENT *pImm;
    char *szTest = "wa"; //ng";
    int i, j;
    long n;
    PhraseItem *p; 
    PhraseItem a; 

    pImm = ImmOp_Ptr.open ("../../datas"); ///pinyin.tab");
                           //"../../datas/ziranma.tab");
    pImm->m.szPhrase = pImm->buf;

    printf ("\n");
    for (i = 0; i < strlen (szTest); i++)
    {
        char buf[256];
        n = ImmOp_Ptr.KeyFilter (pImm, szTest[i]);
        ImmOp_Ptr.GetInputDisplay (pImm, buf, sizeof (buf)),
        printf ("Input::%s\n", buf);
        ImmOp_Ptr.GetSelectDisplay (pImm, buf, sizeof (buf));
        printf ("Selection::%s\n", buf);

        printf ("each select test...\n");
        for (j = 0; j < n; j++)
        {
             p = IMM_pGetItem (pImm, j);
             if (p != NULL)
                 printf ("%s,", p->szPhrase);
        }
        printf ("\n");
    }

    p = ImmOp_Ptr.DoSelectItem (pImm, 0);
    if (p != NULL)
        printf ("you select : 0 ==> %s\n", p->szPhrase);
    printf ("resuming....\n");
    n = ImmOp_Ptr.RestoreToLastStep (pImm);
    for (j = 0; j < n; j++)
    {
        p = IMM_pGetItem (pImm, j);
        if (p != NULL)
            printf ("%s,", p->szPhrase);
    }
    printf ("\n");

    ImmOp_Ptr.PageUp (pImm);
    ImmOp_Ptr.PageDown (pImm);

    ImmOp_Ptr.close (pImm);
    return 1;
}
#endif

