/*
 *
 * 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
 */
/* GB2312:  A1A1-FEFE
 * GBK:	    8140-FEFE
 * GB18030: 81308130-FE39FE39
 */

/*
 Compilation:
 Step1: gcc -g -Wall -I../../include -I. -c xl_intcode.c -o intcode.o
 Step2: gcc -g -shared intcode.o -o intcode.so
*/
/* Imm Standard Interfaces */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <ImmModule.h>
#include "xl_hzinput.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;

  table = malloc (sizeof (hz_input_table));
  if (table == NULL)
    {
      fprintf (stderr,
	       "Out of memory in loading internel code input method\n");
      return NULL;
    }

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

  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);
	}
    }
  return table;
}

int
ConfigureInputArea (HzInputTable_T * pHzInputTable, int nSelectionLen)
{
  pHzInputTable->MaxSelectLen = nSelectionLen;	//Rat: 48
  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)	//GB18030 chars
    {
      Key = (pHzInputTable->InpKey[0] << 28)	//1st inputed key
	| (pHzInputTable->InpKey[1] << 24)	//2nd ik
	| (pHzInputTable->InpKey[2] << 20)	//3rd ik
	| (pHzInputTable->InpKey[3] << 16)	//4th ik
	| (pHzInputTable->InpKey[4] << 12)	//5th ik
	| (pHzInputTable->InpKey[5] << 8)	//6th ik
	| (pHzInputTable->InpKey[6] << 4);	//7th ik
    }
  else				//GB2312/GBK chars
    {
      Key = (pHzInputTable->InpKey[0] << 12)	//1st ik
	| (pHzInputTable->InpKey[1] << 8)	//2nd ik
	| (pHzInputTable->InpKey[2] << 4);	//3rd ik
    }


  switch (pHzInputTable->InputCount)
    {
    case 3:
      if (pHzInputTable->val1 == CODING_DOUBLE_BYTES)	//GB2312/GBK chars
	{
	  pHzInputTable->StartKey = Key;
	  pHzInputTable->EndKey = Key + 0x10;
	}
      else			//GB18030 chars
	pHzInputTable->StartKey = pHzInputTable->EndKey = 0;	//not end yet
      break;

    case 7:			//GB18030 chars
      pHzInputTable->StartKey = Key;	//xxxx xx30
      pHzInputTable->EndKey = Key + 0x0A;	//xxxx xx39
      break;

    default:
      pHzInputTable->StartKey = pHzInputTable->EndKey = 0;
    }
}


void
IntCode_FillMatchChars (HzInputTable_T * pHzInputTable, int index)
{
  int MaxSel = 10, i = 0, k = 0, CurSelNum = 0;
  char temptab[16][16];

  for (i = 0; i < 16; i++)
    temptab[i][0] = '\0';

  if (pHzInputTable->InputCount < 2)	//Don't begin to search now
    return;

  if (pHzInputTable->InputCount == 7)	//GB18030 chars
    {
      while (CurSelNum < MaxSel && index < pHzInputTable->EndKey)
	{
	  temptab[CurSelNum][0] = (char) ((index & 0xFF000000) >> 24);
	  temptab[CurSelNum][1] = (char) ((index & 0x00FF0000) >> 16);
	  temptab[CurSelNum][2] = (char) ((index & 0x0000FF00) >> 8);
	  temptab[CurSelNum][3] = (char) ((index & 0x000000FF));
	  temptab[CurSelNum][4] = '\0';
	  CurSelNum++;
	  index++;
	}

      for (k = 0; k < MaxSel; k++)
	{
	  if (mbtowc (NULL, temptab[k], 4) > 0)
	    strncpy (pHzInputTable->seltab[k], temptab[k], 4);
	  else
	    {
	      pHzInputTable->seltab[k][0] = 0xa1;
	      pHzInputTable->seltab[k][1] = 0xa1;
	      pHzInputTable->seltab[k][2] = 0;
	    }
	}
      pHzInputTable->MultiPageMode = 0;
    }
  else				//GB2312/GBK
    {
      while (CurSelNum < MaxSel && index < pHzInputTable->EndKey)
	{
	  temptab[CurSelNum][0] = index / 256;	//00-FF->0-256
	  temptab[CurSelNum][1] = index % 256;
	  temptab[CurSelNum][2] = '\0';
	  CurSelNum++;
	  index++;
	}

      for (k = 0; k < MaxSel; k++)
	{
	  if (mbtowc (NULL, temptab[k], 2) > 0)
	    strncpy (pHzInputTable->seltab[k], temptab[k], 2);
	  else
	    {
	      pHzInputTable->seltab[k][0] = 0xa1;
	      pHzInputTable->seltab[k][1] = 0xa1;
	      pHzInputTable->seltab[k][2] = 0;
	    }
	}
    }


  pHzInputTable->CurSelNum = CurSelNum;

  for (i = CurSelNum; i < 16; i++)
    pHzInputTable->seltab[i][0] = '\0';


  pHzInputTable->InputMatch = pHzInputTable->InputCount;

  if (index <= pHzInputTable->EndKey && CurSelNum == MaxSel && MaxSel == 10)
    {
      pHzInputTable->NextPageIndex = index;
      pHzInputTable->MultiPageMode = 1;
    }
  else if (pHzInputTable->MultiPageMode)
    {				/* rotate selection */
      pHzInputTable->NextPageIndex = pHzInputTable->StartKey;
    }
  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;

  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);	//searching
	      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 '[':
    case '-':
    case ',':
      if (pHzInputTable->MultiPageMode)	//More than 10 results
	{
	  if (pHzInputTable->CurrentPageIndex > pHzInputTable->StartKey)
	    {
	      pHzInputTable->CurrentPageIndex =
		pHzInputTable->CurrentPageIndex - 10;
	    }
	  else
	    {
	      pHzInputTable->CurrentPageIndex = pHzInputTable->StartKey;
	    }

	  IntCode_FillMatchChars (pHzInputTable,
				  pHzInputTable->CurrentPageIndex);
	}
      else			//Less than 10 results
	return 0;
      break;

    case '>':
    case ']':
    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])	//available
	{
	  strcpy (buff, pHzInputTable->seltab[0]);
	  *len = strlen (buff);
	  ResetInput (pHzInputTable);
	  return 2;
	}
      break;

    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
    case 'a':
    case 'b':
    case 'c':
    case 'd':
    case 'e':
    case 'f':
      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;
	}

      switch (pHzInputTable->InputCount % 4)	//0/4 keys
	{
	case 0:		/* 8-F */
	  if (inkey < 8)	//invalid 1st hexcode of GB2312/GBK/GB18030 Set
	    {
	      return 0;
	    }
	  else
	    {
	      pHzInputTable->InpKey[pHzInputTable->InputCount++] = inkey;
	    }
	  break;

	case 1:		/* 0-F *///1/5 keys
	  if (((pHzInputTable->InpKey[pHzInputTable->InputCount - 1] == 8) && (inkey == 0)) || ((pHzInputTable->InpKey[pHzInputTable->InputCount - 1] == 15) && (inkey == 15)))	//0x80 or 0xFF is invalid
	    {
	      return 0;
	    }
	  else
	    {
	      pHzInputTable->InpKey[pHzInputTable->InputCount++] = inkey;
	    }
	  break;

	case 2:		/* 3-F *///2/6 keys
	  if (inkey < 3)
	    {
	      return 0;
	    }
	  else if (inkey == 3)
	    {
	      pHzInputTable->val1 = CODING_FOUR_BYTES;
	      ClearSelection (pHzInputTable);
	      pHzInputTable->InpKey[pHzInputTable->InputCount++] = inkey;

	      if (pHzInputTable->InputCount == 7)	//4bytes char
		{
		  IntCode_FindMatchKey (pHzInputTable);

		  pHzInputTable->MultiPageMode = 0;
		  pHzInputTable->CurrentPageIndex = pHzInputTable->StartKey;

		  IntCode_FillMatchChars (pHzInputTable,
					  pHzInputTable->StartKey);
		}
	    }
	  else if (pHzInputTable->InputCount == 2)
	    {
	      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);
	    }
	  else
	    {
	      return 0;
	    }
	  break;
	case 3:		/* output char *///3/7 keys
	  /* 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';

		  strcpy (buff, pHzInputTable->seltab[0]);
		  *len = strlen (buff);
		  ResetInput (pHzInputTable);
		  return 2;

		}

	      if ((pHzInputTable->val1 & CODING_FOUR_BYTES) && (pHzInputTable->InputCount == 7))	//only 7 keys
		{
		  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';

		  strcpy (buff, pHzInputTable->seltab[0]);
		  *len = strlen (buff);

		  ResetInput (pHzInputTable);

		  return 2;
		}

	      pHzInputTable->InpKey[pHzInputTable->InputCount++] = inkey;
	    }
	  break;
	}
    default:
      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])	//from 0-MaxSel
	break;

      len = strlen (pHzInputTable->seltab[i]);
      if (pos + len + 3 > nBufLen)
	break;

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

    }

  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 = 0;
  buf[0] = '\0';

  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)
    {
      fprintf (stderr, "Error in malloc for phz_input_table.\n");
      return NULL;
    }

  pHzInputTable_T = (HzInputTable_T *) malloc (sizeof (HzInputTable_T));
  if (pHzInputTable_T == NULL)
    {
      free (phz_input_table);
      fprintf (stderr, "Error in malloc for pHzInputTable.\n");
      return NULL;
    }

  pIMM_CLIENT = (IMM_CLIENT *) malloc (sizeof (IMM_CLIENT));
  if (pIMM_CLIENT == NULL)
    {
      free (pHzInputTable_T);
      free (phz_input_table);
      fprintf (stderr, "Error in malloc for pIMM_CLIENT.\n");
      return NULL;
    };

  ResetInput (pHzInputTable_T);
  ConfigureInputArea (pHzInputTable_T, 48);	//48 is not good
  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)
{
  ResetInput ((HzInputTable_T *) p->pImmClientData);
  return 1;
}

#ifdef  __cplusplus
extern "C"
#endif
struct ImmOperation ImmOp_Ptr = {
  "ÄÚÂë",
  "CJKVInput Version 3.0",
  "Author:Dianzhi Wang",	/* Codes based on HappyConsole from Legend. */
  IMM_CCE | IMM_LC_GB18030 << 24,
  IMM_open,
  IMM_save,
  IMM_close,

  /* Indepent Modules support */
  IMM_KeyFilter,
  IMM_ResetInput,

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

//Test part
#ifdef	__CCE_HZINPUT_DEBUG__
/*
Compilation:
  gcc -g -I../../include -I. -D__CCE_HZINPUT_DEBUG__ GB18030_intcode.c -o intcode_test
Run:
  ./intcode_test
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <xl_hzinput.h>
#include <ImmModule.h>

extern struct ImmOperation ImmOp_Ptr;

int
test (IMM_CLIENT * pImm, char *inbuf, char *type)
{
  int i, j, len;
  long n;
  char buf1[64];

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

      if (n == 2)
	printf ("you select::%s\n", buf1);
      printf ("\n");
    }
}

int
main ()
{
  IMM_CLIENT *pImm;
  char *szTest1 = "8140 ";
  char *szTest2 = "8139ef39 ";

  pImm = ImmOp_Ptr.open (NULL, 0);
  pImm->m.szPhrase = pImm->buf;

  printf ("\n");
  test (pImm, szTest1, "GBK");
  test (pImm, szTest2, "GB18030");
  printf ("\n");
  ImmOp_Ptr.close (pImm);
  return 1;
}
#endif
