/* vi: set sw=4 ts=4: */
/*
 * CCE - Console Chinese Environment -
 * Copyright (C) 1998-2003 Rui He (rhe@3eti.com)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include 	<stdio.h>
#include 	<unistd.h>
#include 	<stdlib.h>
#include	<errno.h>
#include	<ctype.h>
#include	<string.h>
#include	<sys/types.h>

#include 	<defs.h>
#include	<font.h>
#include	<vc.h>
#include 	<hzinput.h>
#include 	<errors.h>
#include	<convert.h>

#define QUWEI_AVAIL(encoding) (encoding == CODE_GB || encoding == CODE_GBK || \
		encoding == CODE_EUC || encoding == CODE_KSC)
#define QUWEI_MAX_1STDIGIT(encoding) (encoding == CODE_GBK ? 7: 9)

hz_input_table* Init_IntCodeInput(hz_input_table *table, int sysCoding)
{
  	int i,index;

   	strcpy(table->cname, IntCode_CNameStr[sysCoding]);

   	for(i = 0; i < 128; i++)
   	{
      	table->KeyMap[i] = -1;
      	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][0] = toupper(i);
		 	table->KeyName[index][1] = '\0';
      	}
   	}    

	table->MaxPress = 4;
   	table->auto_select = 1;
	table->FontMapped = 1; /* using system font */

	table->item = (ITEM *)-1; /* 0 unmapped, -1 (no table) other: mapped */
	table->encoding = sysCoding;
   	table->HZFilter = Intcode_HZFilter;
   	table->RefreshInput = NULL;

	table->InputAreaX = cal_input_xpos(table);

    table->InputAreaWidth = 5; /* 4; // 5 bytes */
    table->SelAreaX = table->InputAreaX + table->InputAreaWidth;
    table->SelAreaWidth = dispInfo.tx_avail - table->SelAreaX 
         - dispInfo.show_terminal * WINDOW_TERMINAL_STR_WIDTH; /* 18 or 0 */

   return table;
}

#define STR_AF 		"abcdef"
#define STR_89AF    "89abcdef"
#define STR_09AF 	"0123456789abcdef"
#define STR_89E     "89e"
#define STR_49AF    "456789abcdef"
#define STR_47AF    "4567abcdef"
 
static unsigned char *ValidCharCode[][4] = { /* coding, valid input string */
       { STR_AF,    STR_09AF,    STR_AF,     STR_09AF }, /* ISO-2022-JP 7 bits, use JIS/EUC */
       { STR_AF,    STR_09AF,    STR_AF,     STR_09AF }, /* EUC-JP */
	   { STR_89E,   STR_09AF,    STR_49AF,   STR_09AF }, /* SJIS */
       { STR_AF,    STR_09AF,    STR_47AF,   STR_09AF }, /* Big5 */
       { STR_AF,    STR_09AF,    STR_AF,     STR_09AF }, /* GB */
       { STR_89AF,  STR_09AF,    STR_49AF,   STR_09AF }, /* GBK */
       { STR_AF,    STR_09AF,    STR_AF,     STR_09AF }, }; 
    
static int ValidIntCode[][4][2] = { /* Coding, InputCount, min/max */
	   { { 0xA, 0xF}, { 0xA1, 0xFE}, { 0xA, 0xF}, { 0xA1, 0xFF} }, /* ISO-2022-JP 7 bits */

	   { { 0xA, 0xF}, { 0xA1, 0xFE}, { 0xA, 0xF}, { 0xA1, 0xFE} }, /* EUC-JP */
	   { { 0x8, 0xE}, { 0x81, 0xEF}, { 0x4, 0xF}, { 0x40, 0xFC } }, /* Shift-JIS */
  
	   { { 0xA, 0xF}, { 0xA1, 0xFE}, { 0x4, 0xF}, { 0x40, 0xFE} }, /* Big5, 2nd Byte 0x7F-A0 invalid */
	   { { 0xA, 0xF}, { 0xA1, 0xFE}, { 0xA, 0xF}, { 0xA1, 0xFE} }, /* GB */
	   { { 0x8, 0xF}, { 0x81, 0xFE}, { 0x4, 0xF}, { 0x40, 0xFE} }, /* GBK, 2nd Byte 0x7F invalid */
	   { { 0xA, 0xF}, { 0xA1, 0xFE}, { 0xA, 0xF}, { 0xA1, 0xFE} }, /* KSC */
	 };

void IntCode_FindMatchKey(int sysCoding)
{
	unsigned int Byte1, Byte2, CandNum;
  
	if (InpKey[0] <= QUWEI_MAX_1STDIGIT(sysCoding) && QUWEI_AVAIL(sysCoding))
	{
		Byte1 = 0xA0 + (InpKey[0]*10 + InpKey[1]); /* A1~FE */
		Byte2 = InpKey[2] * 10 + 0xA0;
		if (Byte2 == 0xA0) 
		{
			Byte2 = 0xA1;  /* make it 0xA1 (for GBK) */
			CandNum = 9;
		}
		else CandNum = 10;
	}
	else 
	{
		Byte1 = (InpKey[0] << 4) | InpKey[1];
		Byte2 = (InpKey[2] << 4);
		CandNum = 16;
	}
   	Byte1 <<= 8;

   switch(InputCount)
   {
      case 0:
      case 1:
         StartIndex = EndIndex = MultiPageMode = 0;    /* not display selection */
         break;
      case 2:
         StartIndex = Byte1 + ValidIntCode[sysCoding][3][0]; /* 0xA1;  GB only */
         EndIndex = Byte1 + ValidIntCode[sysCoding][3][1] + 1; /* 0xFF;  // A1-A9,B0-F7 A1-FE GB only */
         break;
      case 3:
         StartIndex = Byte1 + Byte2; 
         EndIndex = StartIndex + CandNum;
		 if (Byte2 < ValidIntCode[sysCoding][3][0]) 
			 StartIndex = Byte1 + ValidIntCode[sysCoding][3][0];  /* 0xA0->0xA1 */
		 if (Byte2+CandNum-1 > ValidIntCode[sysCoding][3][1]) 
			 EndIndex = Byte1 + ValidIntCode[sysCoding][3][1]+1;
         break;
    }

	IsDirectSelect = 1;
	MultiPageMode = 0;
    CurrentPageIndex = NextPageIndex = StartIndex;
	memset( SelTab, 0, sizeof(SelTab) ); /* reset the selection */

	IntCode_FillMatchChars(sysCoding, StartIndex);
}

void IntCode_FillMatchChars(int sysCoding, int index)
{
    int MaxSel,i, byte1, byte2;
   
	if (index < StartIndex || index >= EndIndex) index = StartIndex;

	CurSelNum = 0;

    if (InputCount <   /* big5/gbk xx4-8x, conflict with the 0-9 selection key */
		(sysCoding == CODE_GBK || sysCoding == CODE_BIG5 || sysCoding == CODE_SJIS ? 3 : 2) ) 
		return;

	if (InpKey[0] <= QUWEI_MAX_1STDIGIT(sysCoding) && QUWEI_AVAIL(sysCoding))
	{
		if (InputCount == 2) return;
		MaxSel = 10;
		if (index % 256 <= 0xA1)  /* 0xA1 or 0xA0 */
			SelTab[CurSelNum++][0] = '\0';
	}	
	else
	{
		if (InputCount == 2) MaxSel = 10; else MaxSel = 16;
	
		if (InputCount == 3 && index % 256 == 0xA1 && 
			 ValidIntCode[sysCoding][3][0] == 0xA1) /* it's possible to be 0x40  */
			SelTab[CurSelNum++][0] = '\0';
	}

    while( CurSelNum < MaxSel && index < EndIndex)
    {
		byte1 = index >> 8;
		byte2 = index & 0xFF;
		if (InputCount == 3 &&  ( byte2 == 0x7F ||  /* GBK/SJIS/Big5 doesn't allow 0x7F */
            ( sysCoding == CODE_BIG5 && byte2 == 0xA0 )  ||
            ( sysCoding == CODE_SJIS && byte1 >= 0xA0 && byte1 <= 0xDF ) ) ) /* SJIS 1st byte 0xA0-DF invalid */
		{
			SelTab[CurSelNum][0] = '\0';
		}
		else
		{
			SelTab[CurSelNum][0] = index >> 8;
         	SelTab[CurSelNum][1] = byte2;
         	SelTab[CurSelNum][2] = '\0';
		}
        CurSelNum++;
        index++;
    }
    
	for(i = CurSelNum; i < 16; i++)
    	SelTab[i][0] = '\0';  /* zero out the unused area */

    InputMatch = InputCount;

    if ( index < EndIndex && CurSelNum == MaxSel && MaxSel == 10)
    {

        NextPageIndex = index;
        MultiPageMode = 1;
    }  
    else if (MultiPageMode)
    {
        NextPageIndex = StartIndex; /* rotate selection */
    }

	DisplaySelection();  /* show it */
}

int Intcode_HZFilter(int tty_fd, unsigned char key)
{
	int inkey = -1;

	if (MultiPageMode)
	{
		if (index(cur_table->pageprevkey, key)) /* not NULL found */
		{
        	CurrentPageIndex -= 10;
			IntCode_FillMatchChars(cur_table->encoding, CurrentPageIndex);
			return 1;
        }
		else if (index(cur_table->pagenextkey, key)

				)
		{
           	CurrentPageIndex = NextPageIndex;
			IntCode_FillMatchChars(cur_table->encoding, CurrentPageIndex);
			return 1;
		}
	}
		
	switch ( key )
	{
    case '\010':  /* BackSpace Ctrl+H */
    case '\177':  /* BackSpace */
		if ( InputCount > 0 ) 
        {
			int tmpSave;

			InpKey[--InputCount]=0; 
			switch(InputCount)
			{
			case 0:
				ResetInput(0,1);
				break;
			case 1: 
				tmpSave = InpKey[0];
				ResetInput(0,1);
				InpKey[0] = tmpSave;
				InputCount = 1;  /* restore the values */
				break;
			case 2:
				ClearSelectionArea(); /* clear it up first */
              	IntCode_FindMatchKey(cur_table->encoding);
				break;
			}
           	DisplayInput();
			return 1; /* we handled it */
        }
		return 0;

	case '\033':  /* ESCAPE */
    	if (InputCount > 0) 
		{
        	ResetInput(1,1);
			return 1; 
		}
		return 0;

	case ' ':
    	if ( CurSelNum == 0 ) return 0;
        
		if ( SelTab[0][0]  || (SelTab[0][0] == '\0' && SelTab[1][0]) ) 
		{
        	OutputSelection(cur_table->encoding, tty_fd, 
					SelTab[0][0] ? SelTab[0] : SelTab[1]);
			return 1;
		}
		break;

     default:
        inkey   = cur_table->KeyMap[key]; /* -1 is invalid 0-15 is valid */
		if (inkey >= 0 && inkey < CurSelNum && SelTab[inkey][0] && InputCount >= 2 )
       	{
            OutputSelection(cur_table->encoding, tty_fd, SelTab[inkey]);
			return 1; /* go back */
       	}
 		
		if (InputCount == 3) return 1; /* not a valid 4th char, ignore it, no output  */
	
		if (inkey < 0) break; /* not 0-9a-f */
		
		if ( QUWEI_AVAIL(cur_table->encoding) &&
			 ((InputCount == 0 && inkey >=0 && 
			   inkey <= QUWEI_MAX_1STDIGIT(cur_table->encoding) ) || 
			  (InputCount > 0 && InpKey[0] <= QUWEI_MAX_1STDIGIT(cur_table->encoding) )) )
		{
			if (inkey > 9) return 1; /* ignore a~f */
			if ( InputCount == 1 ) 
			{
				int byte1 = InpKey[0] * 10 + inkey;
				if (byte1 < 1 || byte1 > 94) return 1; /* invalid */
			}
		}
		else if (strchr(ValidCharCode[cur_table->encoding][InputCount], key)) 
		{
            
			if ( InputCount == 1)
			{
				int byte1 = (InpKey[0] << 4) | inkey;
				
				if (byte1 < ValidIntCode[cur_table->encoding][1][0] || 
					byte1 > ValidIntCode[cur_table->encoding][1][1] )   
					return 1;  /* not a valid 2nd Char, ignore it */
			}
		}
		else break;
	
       	InpKey[InputCount++] = inkey;
       	if (InputCount >= 2)  
           	IntCode_FindMatchKey(cur_table->encoding);
       	DisplayInput();
		
		return 1;  /* processed  */
		
	} /* switch */

	return (InputCount > 0 ? 1 : 0);  /* ignore it if we have something */
}

