/* 
 * hzinput.c 
 */
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "hzinput.h"

static void hz_input_init (HZIPT *pClient);
static void refresh_input_method_area (HZIPT *pClient);

/* private function */
static struct hz_input_table * load_input_method(char *filename)
{
    int fd, nread;
    char magic_number[2];
    struct hz_input_table *table;
  
    table = malloc(sizeof(struct hz_input_table));
    if (table == NULL)
        return NULL;

    fd = open(filename, O_RDWR);
    if (fd == -1)
    {
        free (table);
        return NULL;
    }

    nread = read (fd, &magic_number, sizeof(magic_number));
    if (nread != sizeof(magic_number))
        goto error_return;
    if ((magic_number[0] != 'H') || (magic_number[1] != 'Z'))
        goto error_return;
    nread = read(fd, table, sizeof(struct hz_input_table));
    if (nread != sizeof(struct hz_input_table))
        goto error_return;
    if (table->version != CIT_VERSION)
        goto error_return;
    if ((table->encode != GB_ENCODING) && (table->encode != BIG5_ENCODING))
        goto error_return;

    table->trie_list = 
           calloc(sizeof(struct trie_node), table->trie_list_size);
    table->hz_list = 
           calloc(sizeof(XChar2b), table->hz_list_size);
    if (!table->trie_list || !table->hz_list)
    {
        if (table->trie_list)
            free (table->trie_list);
        if (table->hz_list) 
            free (table->hz_list);
        goto error_return;
    }

    nread = read(fd, table->trie_list, 
  	         table->trie_list_size * sizeof(struct trie_node));
    if (nread != table->trie_list_size * sizeof(struct trie_node))
    {
        free(table->trie_list);
        free(table->hz_list);
        goto error_return;
    }

    nread = read (fd, table->hz_list, 
                  table->hz_list_size * sizeof(XChar2b));
    if (nread != table->hz_list_size * sizeof(XChar2b))
    {
        free(table->trie_list);
        free(table->hz_list);
        goto error_return;
    }

    close(fd);
    return table;

error_return:
    free (table);
    close (fd);
    return NULL;
}

static void free_input_method(struct hz_input_table *table)
{
    free (table->trie_list);
    free (table->hz_list);
    free (table);
}

static void ResetInput (HZIPT *pClient)
{
    pClient->OutputBuf_selection[0] = '\0';
    pClient->OutputBuf_input[0] = '\0';
    pClient->OutputBuf_write[0] = '\0';
}

static void show_selection (HZIPT *pClient)
{
    XChar2b *hzindex;
    int i;
    char *p;  
    char buf[32];

    p = pClient->OutputBuf_selection;
    *p = '\0';
    if (pClient->current_trie_node)
    {
        hzindex = &pClient->input_table->hz_list
                        [pClient->current_trie_node->hz_index + 
                  pClient->choice_start];
        if (pClient->choice_start)
            strcat (p, "< ");
        for (i = 0; 
             (i < pClient->input_table->maxchoice) && 
             ((i + pClient->choice_start) < 
                  pClient->current_trie_node->num_choice); 
             i++, hzindex++)
        {
            sprintf (buf, " %c %c%c", 
                             pClient->input_table->choicelb[i], 
                             hzindex->byte1, hzindex->byte2);
            strcat (p, buf);
        }
        if ((i + pClient->choice_start) != 
            pClient->current_trie_node->num_choice)
            strcat (p, " >");
    }
    pClient->nTotalCurSel = i;
    for (i = 0; i < pClient->num_inputkey; i++)
         pClient->OutputBuf_input[i] = pClient->inputkey_buf[i];
    pClient->OutputBuf_input[i] = '\0';
}

static void WriteStr (HZIPT *pClient, char *str, int len)
{
    memcpy (pClient->OutputBuf_write, str, len);
    pClient->OutputBuf_write[len] = '\0';
}

static struct trie_node * search_trie_node
       (HZIPT *pClient, struct trie_node *node, unsigned char c)
{
    struct trie_node *next_node;
    int i;
  
    next_node = &pClient->input_table->trie_list[node->next_keys];
    for (i = node->num_next_keys; i > 0; i--, next_node++)
    {
        if (next_node->key == c)
            return next_node;
    }
    return NULL;
}


/* public function */
HZIPT *Cxterm_Open (unsigned char * filename)
{
    HZIPT *pClient = (HZIPT *) malloc (sizeof (HZIPT));
    if (pClient == NULL)
        return NULL;
    pClient->input_table = load_input_method(filename);
    hz_input_init (pClient);
    ResetInput (pClient);
    return pClient;
}

void Cxterm_Close (HZIPT *pClient)
{
    free_input_method(pClient->input_table);
    free (pClient);
}

int Cxterm_HzFilter (HZIPT *pClient, unsigned char c, char *buf, int *len)
{
    if ((c == HZKEY_FORCE_SELECT) &&      /* use ESC as key for force select */
        (pClient->trans) && 
        (pClient->num_inputkey))/* no further input key will use as search */
        pClient->force_select = 1;
    else if ((c & 0x80) || (!pClient->trans) || 
	     (pClient->input_table->keytype[c] == HZKEY_INVALID))
    {
        //printf("invalid char: %c\n", c);
        return 0;
    }
    else if (pClient->input_table->keytype[c] & HZKEY_INPUT_MASK)
    {
        if (((pClient->input_table->keytype[c] & HZKEY_SELECTION_MASK) 
              == HZKEY_SELECTION_MASK) && pClient->force_select)
        {
            pClient->force_select = 0;
            goto hz_selection_keypressed;
        }
        if (pClient->num_inputkey < MAX_INPUT_BUF)
        {
            struct trie_node *n = 
                      search_trie_node(pClient, pClient->current_trie_node, c);
            if (n)
            {
                pClient->trie_node_buf[pClient->num_inputkey] = 
                      pClient->current_trie_node;
                pClient->inputkey_buf[pClient->num_inputkey++] = c;
                pClient->choice_start = 0;
                pClient->current_trie_node = n;
                pClient->current_pos += 
                    pClient->input_table->keyprompt[c].prompt_len;
                show_selection(pClient);
            }
            else if ((pClient->input_table->keytype[c] & 
                     HZKEY_SELECTION_MASK) == HZKEY_SELECTION_MASK)
	        goto hz_selection_keypressed;
        }
    }
    else if ((pClient->input_table->keytype[c] & 
             HZKEY_SELECTION_MASK) == HZKEY_SELECTION_MASK)
    {
hz_selection_keypressed:
        c = pClient->input_table->keytype[c] & HZKEY_SELECTION_NUM;
        if ((c + pClient->choice_start) < 
             pClient->current_trie_node->num_choice) 
        {
	    char *ptr = (char *) &pClient->input_table->hz_list
                  [pClient->current_trie_node->hz_index + 
                   pClient->choice_start + c];
            memcpy (buf, ptr, sizeof (XChar2b));
            buf[sizeof (XChar2b)] = '\0';
            // WriteStr (pClient, (char *) ptr, sizeof (XChar2b));
            refresh_input_method_area (pClient);
            *len = strlen (buf);
            return 2;
        }
        else
            return  0;
    }
    else if (pClient->input_table->keytype[c] == HZKEY_DELETEALL)
         refresh_input_method_area (pClient);
    else if ((pClient->input_table->keytype[c] == HZKEY_BACKSPACE))
    {
        if (pClient->num_inputkey)
        {
            pClient->num_inputkey--;
            pClient->current_trie_node = 
                     pClient->trie_node_buf[pClient->num_inputkey];
            pClient->current_pos -= 
      	             pClient->input_table->keyprompt
                     [pClient->inputkey_buf[pClient->num_inputkey]].prompt_len;
            // screen_clear_block(pClient->current_pos, 25, 
            // pClient->input_table->
            // keyprompt[pClient->inputkey_buf[pClient->num_inputkey]].prompt_len, 1, 1);
            show_selection (pClient);
            if (pClient->num_inputkey == 0)
                return 1; // Just indicate that we process this key.
        }
        else
        {
	    //printf("putchar:%c\n", c);
            return 0;
        }
    }
    else if (pClient->input_table->keytype[c] == HZKEY_RIGHT)
    {
        if ((pClient->choice_start + pClient->input_table->maxchoice) < 
            pClient->current_trie_node->num_choice)
        {
            pClient->choice_start += pClient->input_table->maxchoice;
            goto common_code;
        }
    }
    else if (pClient->input_table->keytype[c] == HZKEY_LEFT)
    {
        if (pClient->choice_start >= pClient->input_table->maxchoice)
        {
            pClient->choice_start -= pClient->input_table->maxchoice;
common_code:
            show_selection (pClient);
        }
    }
    return 1; 
}

static void hz_input_init (HZIPT *pClient)
{
    pClient->trans = 1;	/* default is ascii input */
    pClient->force_select = 0;
    pClient->num_inputkey = 0;
    pClient->choice_start = 0;
    pClient->current_pos = pClient->input_table->prompt_len;
    pClient->current_trie_node = pClient->input_table->trie_list;
    pClient->nTotalCurSel = 0;
}

static void refresh_input_method_area (HZIPT *pClient)
{
     pClient->trans = 1;
     if (pClient->trans)
     {
         pClient->num_inputkey = 0;
         pClient->choice_start = 0;
         pClient->current_pos = pClient->input_table->prompt_len;  
         pClient->current_trie_node = pClient->input_table->trie_list;
         pClient->nTotalCurSel = 0;
     }
}

int Cxterm_GetInputDisplay (HZIPT *pClient, char *buf)
{
  int i;
  char buf_tmp[128];
  //  Transfer the key which is input into key prompt of ZOZY and ETZY
  if(pClient->num_inputkey==0 || 
     strlen(pClient->OutputBuf_input)==0) {  
        //if the press SPACE key the display area should be empty.
        sprintf(buf_tmp,"%s","");
  } 
  else {
    for(i=0;i<pClient->num_inputkey;i++) {
      char cc;
      cc=pClient->OutputBuf_input[i];
      if(i==0)
        sprintf(buf_tmp,"%s",pClient->input_table->keyprompt[cc].prompt);
      else
        sprintf(buf_tmp,"%s%s",buf_tmp,pClient->input_table->keyprompt[cc].prompt);
    }
  }
  strcpy (buf, buf_tmp);
  return 1;
}

int Cxterm_GetSelectionDisplay (HZIPT *pClient, char *buf)
{
    strcpy (buf, pClient->OutputBuf_selection);
    return 1;
}

char *szGetSelItem (HZIPT *pClient, int n, char *buf)
{
    int c = n;
    if ((c + pClient->choice_start) <
        pClient->current_trie_node->num_choice)
    {
        char *ptr = (char *) &pClient->input_table->hz_list
             [pClient->current_trie_node->hz_index +
              pClient->choice_start + c];
        memcpy (buf, ptr, sizeof (XChar2b));
        buf[sizeof (XChar2b)] = '\0';
        return buf;
    }
    return  NULL;
}


