/*
	Copyright (C) 1994-2008	Edward Der-Hua Liu, Hsin-Chu, Taiwan
*/


#include "gcin.h"
#include "pho.h"
#include <sys/stat.h>
#include <stdlib.h>
#include "gtab.h"
#include "gst.h"
#include "pho-status.h"

PHO_ST poo;
extern gboolean test_mode;

extern PHO_ITEM *ch_pho;

PHOKBM phkbm;
extern int text_pho_N;
gboolean b_hsu_kbm;
extern PIN_JUYIN *pin_juyin;
int pin_juyinN;

gboolean full_char_proc(KeySym keysym);
void hide_win_pho();
void ClrSelArea();

#define MAX_HASH_PHO 27
u_short hash_pho[MAX_HASH_PHO+1];

char typ_pho_len[]={5, 2, 4, 3};

gboolean same_query_show_pho_win();

gboolean typ_pho_empty3()
{
  return !poo.typ_pho[0] &&!poo.typ_pho[1] &&!poo.typ_pho[2] &&!poo.typ_pho[3];
}

gboolean typ_pho_empty()
{
  return typ_pho_empty3() &&!poo.typ_pho[3];
}

gboolean pho_has_input()
{
  return !typ_pho_empty() || same_query_show_pho_win();
}

phokey_t pho2key(char typ_pho[])
{
  phokey_t key=typ_pho[0];
  int i;

  if (key==BACK_QUOTE_NO)
    return (BACK_QUOTE_NO<<9) | typ_pho[1];

  for(i=1; i < 4; i++) {
    key =  typ_pho[i] | (key << typ_pho_len[i]) ;
  }

  return key;
}

void key_typ_pho(phokey_t phokey, u_char rtyp_pho[])
{
  rtyp_pho[3] = phokey & 7;
  phokey >>= 3;
  rtyp_pho[2] = phokey & 0xf;
  phokey >>=4;
  rtyp_pho[1] = phokey & 0x3;
  phokey >>=2;
  rtyp_pho[0] = phokey;
}

void mask_key_typ_pho(phokey_t *key)
{
  if (poo.typ_pho[0] == BACK_QUOTE_NO)
    return;
  if (!poo.typ_pho[0]) *key &= ~(31<<9);
  if (!poo.typ_pho[1]) *key &= ~(3<<7);
  if (!poo.typ_pho[2]) *key &= ~(15<<3);
  if (!poo.typ_pho[3]) *key &= ~(7);
}

#define TKBM 0
#define MIN_M_PHO 5

// skip table for hsu
static phokey_t skip[]={
	0x1002, //ㄌ2
	0x4604,  //
	0x30, // ㄟ
	0x604, // ㄇ4
	0x600, // ㄇ
	0x400, // ㄆ
	0xc00, // ㄊ
	0x1600,  // ㄏ
	0xe00,  // ㄋ
	0x1400, // ㄎ
	0x1c00, // ㄒ
	0x1200, // ㄍ
	0x1c01, // ㄒ1 pho-huge
	0x1c02, // ㄒ2 pho-huge
	0x1c03, // ㄒ3 pho-huge
	0x1c04, // ㄒ4 pho-huge
	0 // stop
};

#define HUGE_PHO 1

#if HUGE_PHO
static void find_match_phos_(u_char mtyp_pho[4],  int *mcount, int newkey, gboolean mask_tone_only)
#else
static void find_match_phos(u_char mtyp_pho[4],  int *mcount, int newkey)
#endif
{
      int vv;
      phokey_t key = pho2key(poo.typ_pho);
      phokey_t mkey;

	  *mcount = 0;
	  bzero(mtyp_pho, sizeof(poo.typ_pho));

      mask_key_typ_pho(&key);
#if TKBM
      dbg("-------------------- %d --", poo.typ_pho[3]);
      prph(key);
      dbg("\n");
#endif
      for (vv = hash_pho[poo.typ_pho[0]]; vv < hash_pho[poo.typ_pho[0]+1]; vv++) {
        phokey_t ttt=idx_pho[vv].key;
#if TKBM
		prph(ttt);  dbg(" 0x%x\n", ttt);
#endif
		int j;
		for(j=0; skip[j]; j++)
			if (skip[j]==ttt)
				break;
		if (skip[j])
			continue;


        if (newkey!=' ' && !poo.typ_pho[3]) {
#if HUGE_PHO
		  if (mask_tone_only)
			ttt &= ~(7);
		  else
#endif
            mask_key_typ_pho(&ttt);
        }

        if (ttt < key)
          continue;

        if (ttt > key)
          break;

//		dbg(" ");  prph(ttt); dbg(" 0x%x\n", ttt);

        int count = 0;

        int i;
        for(i=idx_pho[vv].start; i < idx_pho[vv+1].start; i++) {
//			dbg("%d %s",vv, pho_idx_str(i));
          if (utf8_sz(pho_idx_str(i)) > 1) {
#if 0
            utf8_putchar(ch_pho[i].ch);
            dbg(" ");
#endif
            count++;
          }
        }

        if (*mcount < count) {
          *mcount = count;
          memcpy(mtyp_pho, poo.typ_pho, sizeof(poo.typ_pho));
#if TKBM
          dbg("count %d\n", count);
#endif

#if 0
          if (*mcount > MIN_M_PHO)
            break;
#endif
        }
      }

      dbg("ret %d\n", *mcount);
}

#if HUGE_PHO
static void find_match_phos(u_char mtyp_pho[4],  int *mcount, int newkey) {
	find_match_phos_(mtyp_pho, mcount, newkey, TRUE);
	*mcount = *mcount*100;
	if (*mcount==0) {
		find_match_phos_(mtyp_pho, mcount, newkey, FALSE);
	}
}
#endif

gboolean inph_typ_pho_pinyin(int newkey);

static int typ_pho_status()
{
  return poo.typ_pho[3] ? PHO_STATUS_OK_NEW:PHO_STATUS_OK;
}

// only for 41 key
int inph_typ_pho_no_tone(KeySym newkey)
{
  dbg("inph_typ_pho_no_tone %d %c\n", newkey, newkey);
  int i;
  int insert = -1;

  if (poo.typ_pho[0]==BACK_QUOTE_NO) {
	poo.typ_pho[1]=(char)newkey;
	poo.inph[1]=newkey;
    return PHO_STATUS_OK|PHO_STATUS_TONE;
  }

  if (newkey==' ') {
//    dbg("zzzz\n");
	if (!poo.typ_pho[0] && !poo.typ_pho[1] && !poo.typ_pho[2])
		return PHO_STATUS_REJECT;
    if (newkey==' ') {
	  u_char mtyp_pho[4];
	  int count;
	  find_match_phos(mtyp_pho, &count, newkey);
//	  dbg("count %d\n", count);
	  if (count > 0)
	    return PHO_STATUS_OK_NEW|PHO_STATUS_TONE;
      return PHO_STATUS_OK_NEW;
    }
    if (poo.typ_pho[0]==BACK_QUOTE_NO && poo.typ_pho[1])
      return PHO_STATUS_OK_NEW | PHO_STATUS_TONE;
//    dbg("ok %d\n", poo.typ_pho[0]);
    return PHO_STATUS_OK;
  }


  int max_in_idx;
  for(max_in_idx=3; max_in_idx>=0 && !poo.typ_pho[max_in_idx]; max_in_idx--);

  // try insert mode first
  for(i=0; i < 3; i++) {
    char num = phkbm.phokbm[(int)newkey][i].num;
    int typ = phkbm.phokbm[(int)newkey][i].typ;

    if (num && !poo.inph[typ] && typ>max_in_idx) {
	  poo.inph[typ] = newkey;
	  poo.typ_pho[typ] = num;
#if TKBM || 0
      dbg("insert typ %d\n", typ);
#endif
      insert = typ;
      if (typ==3) {
		poo.typ_pho[typ] = num;
		return PHO_STATUS_OK_NEW | PHO_STATUS_TONE;
	  }

#if 1
      if (typ==1) {
		if (poo.typ_pho[0]) {
			u_char mtyp_pho[4];
			int count;
			find_match_phos(mtyp_pho, &count, newkey);
			if (!count) {
				poo.typ_pho[1]=0;
				insert=-1;
				break;
			}
		}
	  }
#endif

#if 0
      if (typ==2) {
		if (poo.typ_pho[0]) {
			u_char mtyp_pho[4];
			int count;
			find_match_phos(mtyp_pho, &count, newkey);
			if (!count) {
				poo.typ_pho[2]=0;
				insert=-1;
				break;
			}
		}
	  }
#endif


#if 0
      if (typ==2) {
		poo.typ_pho[typ] = num;
		return PHO_STATUS_OK_NEW;
	  }
#endif
      break;
    }
  }

  if (insert < 0) {
	bzero(poo.inph, sizeof(poo.inph));
	poo.inph[0]=newkey;
//	dbg("PHO_STATUS_PINYIN_LEFT\n");
	return PHO_STATUS_OK_NEW|PHO_STATUS_PINYIN_LEFT;
  }

  return PHO_STATUS_OK;
}


int inph_typ_pho(KeySym newkey)
{
  int i;
  int insert = -1;

  if (pin_juyin) {
    return inph_typ_pho_pinyin(newkey);
  }

  if (pho_no_tone) {
	  return inph_typ_pho_no_tone(newkey);
  }

  if (poo.typ_pho[0]==BACK_QUOTE_NO) {
	poo.typ_pho[1]=(char)newkey;
	poo.inph[1]=newkey;
    return PHO_STATUS_OK;
  }

  int max_in_idx;
  for(max_in_idx=3; max_in_idx>=0 && !poo.typ_pho[max_in_idx]; max_in_idx--);

  // try insert mode first
  for(i=0; i < 3; i++) {
    char num = phkbm.phokbm[(int)newkey][i].num;
    int typ = phkbm.phokbm[(int)newkey][i].typ;

    if (num && !poo.inph[typ] && typ>max_in_idx) {
	  poo.inph[typ] = newkey;
	  poo.typ_pho[typ] = num;
#if TKBM
      dbg("insert typ %d\n", typ);
#endif
      insert = typ;
      break;
    }
  }

  if (insert < 0) {
    // then overwrite mode
    for(i=0; i < 3; i++) {
      char num = phkbm.phokbm[newkey][i].num;
      int typ = phkbm.phokbm[newkey][i].typ;

      if (num) {
        poo.inph[typ] = newkey;
        poo.typ_pho[typ] = num;
        insert = typ;
        break;
      }
    }
  }

//  dbg("newkey %c\n", newkey);

  int mcount = 0;
  u_char mtyp_pho[4];
  char mtyp0=0;

  find_match_phos(mtyp_pho, &mcount, newkey);

//  dbg("mcount %d\n", mcount);

  int a;
  for(a=0; a < 3; a++) {
	int mc=0;
	u_char ttyp[4];
    char num = phkbm.phokbm[(int)poo.inph[0]][a].num;
    char typ = phkbm.phokbm[(int)poo.inph[0]][a].typ;
#if 0
    if (typ == 3)
      continue;
#endif
	if (!num)
		continue;

    if (num && mcount==0) {
      if (typ==2 && poo.typ_pho[0] && !poo.typ_pho[2])
        poo.typ_pho[0] = 0;
      poo.typ_pho[(int)typ] = num;
#if TKBM
      dbg("%d num %d\n",a, num);
#endif
      find_match_phos(mtyp_pho, &mcount, newkey);
    }

	if (mcount==0)
    for(i=0; i < 3; i++) {
      char num = phkbm.phokbm[(int)poo.inph[2]][i].num;
      char typ = phkbm.phokbm[(int)poo.inph[2]][i].typ;

      if (!num)
        break;

      if (typ!=2)
        continue;

      poo.typ_pho[(int)typ] = num;

      find_match_phos(mtyp_pho, &mcount, newkey);
#if 0
      if (mcount > MIN_M_PHO) {
		 memcpy(poo.typ_pho, mtyp_pho, sizeof(poo.typ_pho));
        return typ_pho_status();
      }
#endif
    }

	if (mcount > 0 && typ != mtyp0)
		continue;

	poo.typ_pho[mtyp0] = 0;

	poo.typ_pho[typ]=num;
	find_match_phos(ttyp, &mc, newkey);

	if (mc > mcount) {
		memcpy(mtyp_pho, ttyp, sizeof(ttyp));
		mcount = mc;
		mtyp0 = typ;
	}
  }

  if (mcount) {
    memcpy(poo.typ_pho, mtyp_pho, sizeof(poo.typ_pho));
    return typ_pho_status();
  }

  return PHO_STATUS_REJECT;
}


void clrin_pho()
{
  bzero(poo.typ_pho,sizeof(poo.typ_pho));
  bzero(poo.inph,sizeof(poo.inph));
  poo.maxi=poo.ityp3_pho=0;
  poo.cpg=0;

  if (gcin_pop_up_win && !same_query_show_pho_win()
      && inmd[default_input_method].method_type==method_type_PHO)
    hide_win_pho();
}

void disp_pho(int index, char *phochar);
void clr_in_area_pho()
{
  int i;

  clrin_pho();
  for(i=0; i < text_pho_N; i++)
    disp_pho(i, "  ");
}

void hide_show_pho_in(gboolean show);

static void disp_in_area_pho()
{
  int i;
  if (test_mode)
	  return;

  hide_show_pho_in(!gcin_display_on_the_spot_key());

  text_pho_N = pin_juyin?7:4;
  if (pin_juyin) {
    for(i=0;i<text_pho_N;i++) {
      disp_pho(i, &poo.inph[i]);
    }
  } else {
    for(i=0;i<4;i++) {
      if (i==1 && poo.typ_pho[0]==BACK_QUOTE_NO) {
        disp_pho(i, &poo.inph[1]);
      }
      else
        disp_pho(i, &pho_chars[i][poo.typ_pho[i]*3]);
    }
  }
}

static int qcmp_count(const void *aa, const void *bb)
{
  PHO_ITEM *a = (PHO_ITEM *)aa;
  PHO_ITEM *b = (PHO_ITEM *)bb;

  return b->count - a->count;
}

void disp_pho_sel(char *s);
void minimize_win_pho();

static void ClrPhoSelArea()
{
  disp_pho_sel("");
  minimize_win_pho();
}


extern char *TableDir;
extern char phofname[128];

gboolean get_start_stop_idx(phokey_t key, int *start_i, int *stop_i)
{
  int typ_pho0 = key >> 9;
  int vv=hash_pho[typ_pho0];

  while (vv<idxnum_pho) {
    if (idx_pho[vv].key>=key) break;
    else
      vv++;
  }

  if (vv >= idxnum_pho || idx_pho[vv].key != key)
    return FALSE;

  *start_i=idx_pho[vv].start;
  *stop_i=idx_pho[vv+1].start;

  return TRUE;
}

// given the pho key & the utf8 char, return the idx in ch_pho
int ch_key_to_ch_pho_idx(phokey_t phkey, char *utf8)
{
  int start_i, stop_i;

  if (!get_start_stop_idx(phkey, &start_i, &stop_i))
	return -1;

  int i;
  for(i=start_i; i<stop_i; i++) {
    char *ch = pho_idx_str(i);
#if 0    
    int u8len = utf8_sz(ch);
    if (!memcmp(ch, utf8, u8len)) 
#else
      if (!strcmp(ch, utf8))
#endif    
    {
      return i;
    }
  }

//  prph(phkey);
//  dbg("error found   %c%c", *big5, *(big5+1));
  return -1;
}

// need this because android's qsort is not stable
 static void my_sort(void *base, size_t nmemb, size_t size,
                  int(*compar)(const void *, const void *))
{
  char t[128];
  int i;
  for(i=0;i<nmemb-1;i++) {
    void *pi = (char *)base + i * size;
    int j;
    for(j=i+1;j<nmemb;j++)  {
      void *pj = (char *)base + j * size;
      if (compar(pi, pj) <= 0)
        continue;

      memcpy(t, pi, size);
      memcpy(pi, pj, size);
      memcpy(pj, t, size);
    }
  }
}

#if WIN32
#include <share.h>
#endif

static FILE *fw;

void close_pho_fw()
{
	if (!fw)
		return;
	fclose(fw);
	fw = NULL;
}

void inc_pho_count(phokey_t key, int ch_idx)
{
  int start_i, stop_i;

  if (!phonetic_char_dynamic_sequence)
    return;

  get_start_stop_idx(key, &start_i, &stop_i);

//  dbg("start_i %d %d    %d %d\n", start_i, stop_i, poo.start_idx, poo.stop_idx);

  ch_pho[ch_idx].count++;
//  dbg("count %d\n", ch_pho[ch_idx].count);

#if 0
  qsort
#else
  my_sort
#endif
  (&ch_pho[start_i], stop_i - start_i, sizeof(PHO_ITEM), qcmp_count);

#if 0
  int i;
  for(i=start_i; i < stop_i; i++) {
    dbg("uuuu %c%c%c %d\n", ch_pho[i].ch[0], ch_pho[i].ch[1],
      ch_pho[i].ch[2], ch_pho[i].count);
  }
#endif

//  dbg("phofname %s\n", phofname);
#if UNIX
  if (!fw && (fw=fopen(phofname,"rb+"))==NULL)
#else
if (!fw && (fw=_fsopen(phofname,"rb+", _SH_DENYWR))==NULL)
#endif
  {
    p_err("err %s\n", phofname);
  }

  if (fseek(fw, ch_pho_ofs + sizeof(PHO_ITEM) * start_i, SEEK_SET) < 0)
    p_err("fseek err");
#if 1
  if (fwrite(&ch_pho[start_i], sizeof(PHO_ITEM), stop_i - start_i, fw) <= 0)
    p_err("fwrite err");
  fflush(fw);
#endif

#if 0
  fclose(fw);
#endif
}


void lookup_gtab(char *ch);
gboolean is_gtab_query_mode();
void set_gtab_target_displayed();

#include "gtab-buf.h"

void putkey_pho(u_short key, int idx)
{
  dbg("putkey_pho test_mode:%d idx:%d\n", test_mode, idx);
  if (test_mode)
	  return;

  char *pho_str = pho_idx_str(idx);
  dbg("pho_str %s\n", pho_str);
  if (poo.same_pho_query_state==SAME_PHO_QUERY_pho_select && ggg.gbufN)
    insert_gbuf_nokey(pho_str);
  else {
    send_text(pho_str);
  }

  lookup_gtab(pho_str);

  inc_pho_count(key, idx);

  clr_in_area_pho();
  ClrSelArea();
  ClrPhoSelArea();

  if (is_gtab_query_mode())
    set_gtab_target_displayed();
}

void load_pin_juyin();
void recreate_win1_if_nessary();

void load_tab_pho_file()
{
  close_pho_fw();
  pho_load();

  bzero(poo.typ_pho,sizeof(poo.typ_pho));

  u_int ttt=0;
  int i;
  for(i=0; i<MAX_HASH_PHO; i++) {
    if (idx_pho[ttt].key >> 9 == i)
      hash_pho[i]=ttt;
    else {
      continue;
    }

    while (ttt < idxnum_pho && idx_pho[ttt].key >> 9 == i)
      ttt++;
  }

  for(i=MAX_HASH_PHO; !hash_pho[i];  i--)
    hash_pho[i]=idxnum_pho;

  char kbmfname[MAX_GCIN_STR];
  FILE *fr;

  free(pin_juyin);
  pin_juyin = NULL;

  if (!strstr(pho_kbm_name, "pinyin")) {
    text_pho_N = 4;
  } else {
    load_pin_juyin();
  }

  if (strcmp(pho_kbm_name, "hsu"))
    b_hsu_kbm = FALSE;
  else
    b_hsu_kbm = TRUE;

  char pho_kbm_name_kbm[128];

  strcat(strcpy(pho_kbm_name_kbm, pho_kbm_name), ".kbm");
  dbg("phokbm_name: %s\n", pho_kbm_name_kbm);

  get_sys_table_file_name(pho_kbm_name_kbm, kbmfname);

  if ((fr=fopen(kbmfname,"rb"))==NULL)
     p_err("Cannot open %s", kbmfname);

  dbg("kbmfname %s\n", kbmfname);

  int n = fread(&phkbm,sizeof(phkbm),1,fr);
  fclose (fr);
  phkbm.selkeyN = strlen(pho_selkey);

  dbg("pho_selkey %s\n", pho_selkey);

  recreate_win1_if_nessary();
#if 0
  for(i='A'; i <= 'z'; i++)
    dbg("%c %d %d\n", i, phkbm.phokbm[i][0].num, phkbm.phokbm[i][0].typ);
#endif
}


void show_win_pho();

void init_tab_pho()
{
  if (!ch_pho) {
    load_tab_pho_file();
  }

  show_win_pho();
  clr_in_area_pho();
}

static char *pho_idx_str_markup(int ii)
{
  char *pho_str = pho_idx_str(ii);
  if (!strcmp(pho_str, "<"))
    pho_str = "&lt;";
  else
  if (!strcmp(pho_str, ">"))
    pho_str = "&gt;";
  return pho_str;
}


gboolean shift_char_proc(KeySym key, int kbstate);
gboolean pre_punctuation(KeySym xkey);
void pho_play(phokey_t key);
void close_gtab_pho_win();
gboolean pre_punctuation_hsu(KeySym xkey);
void case_inverse(KeySym *xkey, int shift_m);

int feedkey_pho(KeySym xkey, int kbstate)
{
  int ctyp = 0;
#if 0
  static unsigned int vv, ii;
  static phokey_t key;
#endif
  char *pp=NULL;
  char kno;
  int i,j,jj=0,kk=0;
  char out_buffer[512];
  int out_bufferN;
  int shift_m=kbstate&ShiftMask;
  int ctrl_m=kbstate&ControlMask;

  dbg("%d ------------- feedkey_pho %d %d\n",test_mode, poo.ityp3_pho, poo.cpg);

  if (ctrl_m)
    return 0;


  if (kbstate&LockMask) {
    if (xkey >= 0x7e || xkey < ' ')
      return FALSE;
    if (gcin_capslock_lower)
      case_inverse(&xkey, shift_m);
	if (!test_mode)
		send_ascii(xkey);
    return 1;
  }

  if (xkey >= 'A' && xkey <='Z' && poo.typ_pho[0]!=BACK_QUOTE_NO)
    xkey+=0x20;

  switch (xkey) {
    case XK_Escape:
      if (typ_pho_empty())
        return 0;
      ClrPhoSelArea();
      clr_in_area_pho();
      if (is_gtab_query_mode())
        close_gtab_pho_win();
      return 1;
    case XK_BackSpace:
      poo.ityp3_pho=0;
      for(j=3;j>=0;j--) if (poo.typ_pho[j]) {
        poo.typ_pho[j]=0;
        if (typ_pho_empty()) {
          ClrSelArea();
          clr_in_area_pho();
          return 1;
        }
        break;
      }

      if (j<0)
        return 0;

      goto llll3;
    case '<':
       if (!poo.ityp3_pho && tsin_shift_punc) {
         return pre_punctuation(xkey);
       }
       if (poo.cpg >= phkbm.selkeyN)
         poo.cpg -= phkbm.selkeyN;
       goto proc_state;
    case ' ':
      if (!poo.typ_pho[0] && !poo.typ_pho[1] && !poo.typ_pho[2]) {
        if (current_CS->b_half_full_char)
          return full_char_proc(xkey);
        return 0;
      }

//      dbg("poo.ityp3_pho %d\n", poo.ityp3_pho);
      if (!poo.ityp3_pho) {
        poo.ityp3_pho = TRUE;
        goto lll1;
      }

      poo.ii = poo.start_idx+ poo.cpg + phkbm.selkeyN;

      if (poo.ii < poo.stop_idx) {
        poo.cpg += phkbm.selkeyN;
        dbg("spc pool.cpg %d\n", poo.cpg);
      } else {
        if (poo.cpg) {
          poo.cpg=0;
          poo.ii=poo.start_idx;
        } else {
          putkey_pho(poo.key, poo.start_idx);
          return 1;
        }
      }

      goto disp;
   default:
      if (xkey >= 127 || xkey < ' ')
        return 0;

      if (shift_m && tsin_shift_punc) {
//        return shift_char_proc(xkey, kbstate);
        if (pre_punctuation(xkey))
          return 1;
        return 0;
      }

//    dbg("poo.maxi:%d  %d\n", poo.maxi, poo.cpg);

      if ((pp=strchr(pho_selkey, xkey)) && poo.maxi && poo.ityp3_pho) {
        int c=pp-pho_selkey;

        if (c<poo.maxi) {
          putkey_pho(poo.key, poo.start_idx + poo.cpg + c);
        }
        return 1;
      }

      if (poo.ityp3_pho && !poo.cpg) {
        dbg("poo.start_idx: %d\n", poo.start_idx);
        putkey_pho(poo.key, poo.start_idx);
      }

//      poo.cpg=0;
  }

lll1:
  inph_typ_pho(xkey);
//  dbg("typ_pho %x %x\n", poo.typ_pho[0], poo.typ_pho[1]);

  if (gcin_pop_up_win)
    show_win_pho();

  if (poo.typ_pho[3])
    ctyp = 3;

  jj=0;
  kk=1;
llll2:
  if (ctyp == 3) {
       poo.ityp3_pho=1;  /* last key is entered */
  }
llll3:

  poo.key = pho2key(poo.typ_pho);

#if    0
  dbg("poo.typ_pho %d %d %d %d\n", poo.typ_pho[0], poo.typ_pho[1], poo.typ_pho[2], poo.typ_pho[3]);
#endif
  if (!poo.key) {
    return pre_punctuation_hsu(xkey);
  }

  pho_play(poo.key);

  poo.vv=hash_pho[poo.typ_pho[0]];
  phokey_t ttt;
  ttt=0xffff;

  while (poo.vv < idxnum_pho) {
    ttt=idx_pho[poo.vv].key;
    mask_key_typ_pho(&ttt);

    if (ttt>=poo.key)
      break;
    else
      poo.vv++;
  }

//  dbg("vv %d %d\n", vv, idxnum_pho);

  if (ttt > poo.key || (poo.ityp3_pho && idx_pho[poo.vv].key != poo.key) ) {
//    dbg("not found\n");
    while (jj<4) {
      while(kk<3)
        if (phkbm.phokbm[(int)poo.inph[jj]][kk].num ) {

          if (kk) {
            ctyp=phkbm.phokbm[(int)poo.inph[jj]][kk-1].typ;
            poo.typ_pho[ctyp]=0;
          }

          kno=phkbm.phokbm[(int)poo.inph[jj]][kk].num;
          ctyp=phkbm.phokbm[(int)poo.inph[jj]][kk].typ;
          poo.typ_pho[ctyp]=kno;
          kk++;
          goto llll2;
        }
        else
          kk++;
      jj++;
      kk=1;
    }

    bell();
	poo.ityp3_pho=poo.typ_pho[3]=0;
	disp_in_area_pho();

    return 1;
  }

proc_state:
  disp_in_area_pho();
  poo.start_idx = poo.ii = idx_pho[poo.vv].start;
  poo.stop_idx = idx_pho[poo.vv+1].start;

//  dbg("poo.start_idx: %d %d\n", poo.start_idx, poo.stop_idx);

  if (poo.typ_pho[0]==L_BRACKET_NO||poo.typ_pho[0]==R_BRACKET_NO || poo.typ_pho[0]==BACK_QUOTE_NO && poo.typ_pho[1])
     poo.ityp3_pho = 1;

  poo.ii+=poo.cpg;

  if (poo.ityp3_pho && poo.stop_idx - poo.start_idx==1) {
    putkey_pho(poo.key, poo.ii);
    poo.maxi=poo.ityp3_pho=0;
    return 1;
  }

disp:
  if (test_mode)
	  return 1;

  i=0;
  out_bufferN=0;
  out_buffer[0]=0;

  if (poo.ityp3_pho) {
//    dbg("poo.cpg %d\n", poo.cpg);

    while(i< phkbm.selkeyN  && poo.ii < poo.stop_idx) {
      char tt[512];
      sprintf(tt, "<span foreground=\"%s\">%c</span>",
         gcin_sel_key_color, pho_selkey[i]);
      int ttlen = strlen(tt);
      memcpy(out_buffer+out_bufferN, tt, ttlen);
      out_bufferN+=ttlen;
//      strcat(out_buffer, tt);
      char *pho_str = pho_idx_str_markup(poo.ii);
      int len = strlen(pho_str);
      memcpy(&out_buffer[out_bufferN], pho_str, len);
      out_bufferN+=len;
#if 0
      out_buffer[out_bufferN++] = ' ';
#endif
      poo.ii++;
      i++;
    }

    char *tt = poo.cpg ? "&lt;" : " ";
    int ttlen = strlen(tt);
    memcpy(out_buffer+out_bufferN, tt, ttlen);
    out_bufferN+=ttlen;

    if (poo.ii < poo.stop_idx) {
      out_buffer[out_bufferN++] = poo.cpg ? '\\' : ' ';
      tt = "&gt;";
      ttlen = strlen(tt);
      memcpy(out_buffer+out_bufferN, tt, ttlen);
      out_bufferN+=strlen(tt);
    }

    poo.maxi=i;
  } else {
    while(i<phkbm.selkeyN  && poo.ii < poo.stop_idx) {
      char *pho_str = pho_idx_str_markup(poo.ii);
      int len = strlen(pho_str);
      memcpy(&out_buffer[out_bufferN], pho_str, len);
      out_bufferN+=len;

      poo.ii++;
      i++;
    }
    poo.maxi=i;
  }

  out_buffer[out_bufferN]=0;
  disp_pho_sel(out_buffer);

  return 1;
}

static char typ_pho_no_to_xkey(int typ, u_char num)
{
  int i, j;

  for(i=' '; i < 127; i++)
    for(j=0; j < 3; j++)
      if (phkbm.phokbm[i][j].typ == typ && phkbm.phokbm[i][j].num == num)
        return i;

  return 0;
}


void start_gtab_pho_query(char *utf8)
{
  phokey_t phokeys[32];
  int phokeysN, i;

  phokeysN = utf8_pho_keys(utf8, phokeys);
  if (phokeysN <= 0)
    return;

  u_char rtyp_pho[4];
  bzero(rtyp_pho, sizeof(rtyp_pho));
  key_typ_pho(phokeys[0], rtyp_pho);

  char xkeys[4];
  bzero(xkeys, sizeof(xkeys));

  for(i=0; i < 4; i++) {
    if (!rtyp_pho[i])
      continue;

    xkeys[i] = typ_pho_no_to_xkey(i, rtyp_pho[i]);
  }

  if (!xkeys[3])
    xkeys[3] = ' ';

  for(i=0; i < 4; i++) {
    feedkey_pho(xkeys[i], 0);
  }
}


void pho_reset()
{
}

#if 0
int feedkey_pho_release(KeySym xkey, int kbstate)
{
  return 0;
}
#endif

#include "im-client/gcin-im-client-attr.h"
extern GtkWidget *gwin_pho;
int get_in_area_pho_tsin_str(char *out);

int pho_get_preedit(char *str, GCIN_PREEDIT_ATTR attr[], int *cursor, int *sub_comp_len)
{
#if WIN32 || 1
  *sub_comp_len = !typ_pho_empty();;
  if (gwin_pho && GTK_WIDGET_VISIBLE(gwin_pho))
    *sub_comp_len|=2;
#endif
  int attrN=0;
#if DEBUG && 0
  dbg("tsin_get_preedit\n");
#endif
  str[0]=0;
  if (gcin_display_on_the_spot_key())
    get_in_area_pho_tsin_str(str);

#if DEBUG && 0
  dbg("'%s'\n", str);
#endif

  *cursor = 0;
#if 0
#if WIN32 || 1
  *comp_flag = !typ_pho_empty();
  if (gwin1 && GTK_WIDGET_VISIBLE(gwin1))
    *comp_flag|=2;
#if 1
  if (tss.c_len && !ap_only)
	*comp_flag|=4;
#endif
#endif
#endif

  return attrN;
}

#if WIN32 || 1
static PHO_ST temp_pho_st;
void pho_save_gst()
{
  temp_pho_st = poo;
}

void pho_restore_gst()
{
  poo = temp_pho_st;
}
#endif
