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

#include "big5con.h"

#ifndef	SEEK_SET
#define SEEK_SET 0
#endif

#define U_short(tt) (*(u_short *)(tt))
#define KeyBits (6)

#define b2cpy(a,b) memcpy(a,b,2)
#define MAX_CIN_PHR (40)

typedef struct {
    ITEM           *tbl;
    u_char          quick1[46][10][2];
    int             use_quick;
    char            cname[16];
    u_char          keycol[50];
    int             KeyS;	/* number of keys needed */
    int             MaxPress;	/* Max len of keystrike  ar30:4  changjei:5 */
    int             DefChars;	/* defined chars */
    u_char          keyname[128];
    u_short         idx1[51];
    u_char          keymap[128];
    char            selkey[12];
    u_char         *sel1st;
    int             M_DUP_SEL;
    FILE           *fp, *fphr;
    u_short         revidx[256];
    int             phrnum;
}               INMD;
static INMD     inmd[10], *cur_inmd;
static int      spc_pressed = 0;
static char     seltab[12][MAX_CIN_PHR];
static int      defsel = 0;
static u_long   inch[5];
static int      ci;
static int      last_full, last_idx;
static int      more_pg, pg_idx, m_pg_mode;
static int      sel1st_i;
int             wild_mode;
int             wild_page;
extern int      cursor_x;

/* for array30-like quick code */
static char     keyrow[] =
"qwertyuiop"
"asdfghjkl;"
"zxcvbnm,./";
#define key_col(cha) ( ((u_long)strchr(keyrow, cha) -(u_long)keyrow)%10)

void 
ClrSelArea()
{
    int             i;
    gotox(SelAreaX);
    for (i = 0; i < MCOL; i++)
	xprintf(" ");
    gotox(SelAreaX);
}


static void 
ClrInArea()
{
    int             i;
    gotox(InAreaX);
    set_att(InAreaColor);
    for (i = 0; i < cur_inmd->MaxPress; i++)
	xprintf("  ");
    set_att(NormalColor);
    for (; i < 5; i++)
	xprintf("  ");
    last_idx = 0;
}

static void 
ClrIn()
{
    bzero(inch, sizeof(inch));
    bzero(seltab, sizeof(seltab));
    m_pg_mode = pg_idx = more_pg = wild_mode = wild_page = last_idx = defsel =
	spc_pressed = ci = 0;
    sel1st_i = 15;
}

static void 
DispInArea()
{
    int             i;

    gotox(InAreaX);
    set_att(InAreaColor);
    for (i = 0; i < ci; i++)
	xprintf("%c%c",
		cur_inmd->keyname[inch[i] << 1],
		cur_inmd->keyname[(inch[i] << 1) + 1]);
    if (i < cur_inmd->MaxPress)
	xprintf("  ");
    set_att(NormalColor);
}

static void 
reset_inp()
{
    ClrIn();
    ClrInArea();
    ClrSelArea();
    last_full = 0;
}

extern char    *tabfname[], TabDir[];
extern int      show_keys[];
extern          show_keys_n;

void 
init_tab(int inmdno, int usenow)
{
    FILE           *fp;
    char            ttt[128], uuu[128];
    int             i;
    INMD           *inp = &inmd[inmdno];
    struct TableHead th;

    if (inp->tbl) {
	gotox(0);
	xprintf("%s", inp->cname);
	cur_inmd = inp;
	reset_inp();
	ClrInArea();
	DispInArea();
	return;			/* table is already loaded */
    }
    strcpy(ttt, TabDir);
    if ((fp = fopen(strcat(ttt, tabfname[inmdno]), "r")) == NULL) {
	error("init_tab:1 err open %s", ttt);
	return;
    }
    strcpy(uuu, ttt);

    fread(&th, 1, sizeof(th), fp);
    fread(ttt, 1, th.KeyS, fp);
    fread(inp->keyname, 2, th.KeyS, fp);
    inp->keyname[61 * 2] = ' ';	/* for wild card */
    inp->keyname[61 * 2 + 1] = '?';
    inp->keyname[62 * 2] = ' ';
    inp->keyname[62 * 2 + 1] = '*';
    inp->KeyS = th.KeyS;
    inp->MaxPress = th.MaxPress;
    inp->DefChars = th.DefC;
    strcpy(inp->selkey, th.selkey);
    inp->M_DUP_SEL = th.M_DUP_SEL;

    for (i = 0; i < th.KeyS; i++) {
	inp->keymap[ttt[i]] = i;
	inp->keymap[toupper(ttt[i])] = i;
	inp->keycol[i] = key_col(ttt[i]);
    }

    if ((inp->fp = fopen(strcat(uuu, ".rev"), "r")) == NULL) {
	error("init_tab:2 err open %s.rev", uuu);
	error("Wildcard & code lookup will not function");
    } else {
	show_keys[show_keys_n++] = inmdno;
	fread(inp->revidx, 2, 256, inp->fp);
    }

    if (strlen(th.cname) > 8)
	strcpy(ttt, th.cname);
    else {
	ttt[0] = ' ';
	strcpy(&ttt[1], th.cname);
    }
    strcpy(inp->cname, ttt);

    inp->keymap[(int)'?'] = 61;
    inp->keymap[(int)'*'] = 62;
    fread(inp->idx1, 2, th.KeyS + 1, fp);
    /* printf("chars: %d\n",th.DefC); */
    if ((inp->tbl = (ITEM *) malloc(sizeof(ITEM) * th.DefC)) == NULL) {
	error("malloc err");
	return;
    }
#if	0
    printf("init %s\n", tabfname[inmdno]);
#endif	/* 0 */
    fread(inp->tbl, sizeof(ITEM), th.DefC, fp);
#if	0
    printf("KeyNum:%d MaxPress:%d\n", th.KeyS, th.MaxPress);
#endif	/* 0 */
    fclose(fp);

    strcpy(ttt, TabDir);
    strcat(ttt, tabfname[inmdno]);
    strcat(ttt, ".quick");
    if (fp = fopen(ttt, "r")) {
	fread(inp->quick1, 2, (th.KeyS - 1) * 10, fp);
	inp->use_quick = 1;
	fclose(fp);
    }
    strcpy(ttt, TabDir);
    strcat(ttt, tabfname[inmdno]);
    strcat(ttt, ".sel1st");
    if (fp = fopen(ttt, "r")) {
	struct stat     fst;
	int             fsize;
	fstat(fileno(fp), &fst);
	fsize = fst.st_size;
	if (inp->sel1st)
	    free(inp->sel1st);
	inp->sel1st = (u_char *) malloc(fsize + 2);
	fread(inp->sel1st, 1, fsize, fp);
	inp->sel1st[fsize] = inp->sel1st[fsize + 1] = 0;
	fclose(fp);
    }
    strcpy(ttt, TabDir);
    strcat(ttt, tabfname[inmdno]);
    strcat(ttt, ".phr");
    if (inp->fphr = fopen(ttt, "r")) {
	fread(&inp->phrnum, 4, 1, inp->fphr);
    }
    if (usenow) {
	cur_inmd = inp;
	reset_inp();
	gotox(0);
	xprintf("%s", inp->cname);
	gotox(InAreaX);
	set_att(InAreaColor);
	ClrInArea();
	DispInArea();
    }
}

static void 
putstr_inp(u_char * p)
{
    if (p[2])
	putstr(p);
    else
	sendkey_b5(p);
    ClrIn();
    ClrSelArea();
    ClrInArea();
}

void 
b52key(int inmdno, u_char * ch, u_char * out)
{
    u_long          kkk;
    u_short         ofs, ofs1;
    int             k1, k2, k3, k4, k5, slen, len;
    INMD           *inp = &inmd[inmdno];
    char           *kname = inp->keyname;
    FILE           *fp = inp->fp;
    ITEM            it;
    extern          cur_input_ch[];

    *out = 0;
    if (!fp)
	init_tab(inmdno, 0);
    ofs = inp->revidx[ch[0]];
    ofs1 = inp->revidx[ch[0] + 1];
    fseek(fp, ofs * sizeof(ITEM) + 256 * 2, 0);

    slen = 0;
    while (ofs < ofs1) {
	if (fread(&it, 1, sizeof(ITEM), fp) != sizeof(ITEM))
	    break;
	ofs++;
	if (it.ch[1] > ch[1])
	    break;
	if (it.ch[1] != ch[1] || it.ch[0] != ch[0])
	    continue;
	kkk = CONVT(it.key);
	k1 = (kkk >> 24) & 0x3f;
	k2 = (kkk >> 18) & 0x3f;
	k3 = (kkk >> 12) & 0x3f;
	k4 = (kkk >> 6) & 0x3f;
	k5 = (kkk) & 0x3f;
#define t0(k) kname[k<<1]
#define t1(k) kname[(k<<1)+1]
	if (k1) {
	    out[slen++] = t0(k1);
	    out[slen++] = t1(k1);
	}
	if (k2) {
	    out[slen++] = t0(k2);
	    out[slen++] = t1(k2);
	}
	if (k3) {
	    out[slen++] = t0(k3);
	    out[slen++] = t1(k3);
	}
	if (k4) {
	    out[slen++] = t0(k4);
	    out[slen++] = t1(k4);
	}
	if (k5) {
	    out[slen++] = t0(k5);
	    out[slen++] = t1(k5);
	}
	slen++;
    }
#undef t0
#undef t1
}

#define swap(a,b) { tt=a; a=b; b=tt; }

static u_long   vmask[] =
{0,
    0x3f << 24, (0x3f << 24) | (0x3f << 18), (0x3f << 24) | (0x3f << 18) | (0x3f << 12),
    (0x3f << 24) | (0x3f << 18) | (0x3f << 12) | (0x3f << 6),
    (0x3f << 24) | (0x3f << 18) | (0x3f << 12) | (0x3f << 6) | 0x3f
};

qcmp_b5(u_char * a, u_char * b)
{
    return strcmp(a, b);
}

/* for strict match, use stack */
void 
wildcard()
{
    int             i, j, t, vv, match, wild_ofs = 0;
    u_long          kk;
    ITEM            it;
    int             found = 0;
    FILE           *fp;

    if (!(fp = cur_inmd->fp))
	return;

    ClrSelArea();
    bzero(seltab, sizeof(seltab));
    /* printf("wild %d %d %d %d\n", inch[0], inch[1], inch[2], inch[3]); */
    defsel = 0;
    gotox(SelAreaX + 1);
    fseek(fp, 256 * 2, SEEK_SET);
    for (t = 0; t < cur_inmd->DefChars && defsel < 10; t++) {
	bzero(&it, sizeof(it));
	fread(&it, 1, sizeof(it), fp);
	kk = CONVT(it.key);
	match = 1;
	for (i = 0; i < ci && match && kk;)
	    switch (inch[i]) {
	    case 61:		/* ? */
		kk = (kk << 6) & vmask[4];
		i++;
		break;
	    case 62:		/* * */
		if (i == ci - 1) {
		    kk = 0;
		    i++;
		    break;
		}
		if (inch[i + 1] == '*' || inch[i + 1] == '?') {
		    i += 2;
		    continue;
		}
		do {
		    vv = (kk >> 24) & 0x3f;
		    kk = (kk << 6) & vmask[4];
		} while (vv && vv != inch[i + 1]);
		if (vv != inch[i + 1])
		    match = 0;
		if (i == ci - 2 && kk)
		    continue;
		i += 2;
		continue;
	    default:
		if (inch[i] != ((kk >> 24) & 0x3f)) {
		    match = 0;
		    break;
		}
		kk = (kk << 6) & vmask[4];
		i++;
	    }			/* switch */
	if (i == ci && match && !(kk & vmask[4])) {
	    if (wild_ofs >= wild_page) {
		xprintf("%d%c%c", (defsel + 1) % 10, it.ch[0], it.ch[1]);
		cursor_x++;
		b2cpy(seltab[defsel++], it.ch);
	    } else
		wild_ofs++;
	    found = 1;
	}
    }				/* for t */
    if (!found)
	bell();
}

static char    *
is_selkey(int key)
{
    return strchr(cur_inmd->selkey, key);
}

static void 
load_phr(int j, char *tt)
{
    FILE           *fp = cur_inmd->fphr;
    int             ofs[2], len;
    int             phrno = ((int)(cur_inmd->tbl[j].ch[0]) << 8) |
    cur_inmd->tbl[j].ch[1];
    fseek(fp, (phrno + 1) * 4, SEEK_SET);
    fread(ofs, 4, 2, fp);
    len = ofs[1] - ofs[0];
    if (len > 128 || len <= 0) {
	error("phrae error %d\n", len);
	strcpy(tt, "err");
	return;
    }
    ofs[0] += (cur_inmd->phrnum + 1) * 4;
    fseek(fp, ofs[0], SEEK_SET);
    fread(tt, 1, len, fp);
    tt[len] = 0;
}

feedkey(int key)
{
    int             i, j;
    static u_long   val;
    static int      s1, e1;
    int             inkey;
    int             exa_match;
    char           *iselkey = (char *)0;

    switch (key) {
    case VK_BackSpace:
#if	DELETE_K
    case VK_Delete:
#endif
	last_idx = 0;
	if (ci == 0)
	    return 0;
	if (ci > 0)
	    inch[--ci] = 0;
	wild_mode = spc_pressed = 0;
	if (ci == 1 && cur_inmd->use_quick) {
	    int             i;
	    bzero(seltab, sizeof(seltab));
	    for (i = 0; i < 10; i++)
		memcpy(seltab[i], &cur_inmd->quick1[inch[0] - 1][i][0], 2);
	    defsel = 10;
	    DispInArea();
	    goto Disp_opt;
	}
	break;
    case VK_Escape:
	if (ci) {
	    ClrSelArea();
	    ClrInArea();
	    ClrIn();
	    return 1;
	} else
	    return 0;
    case '<':
	if (wild_mode) {
	    if (wild_page >= 10)
		wild_page -= 10;
	    wildcard();
	    return 1;
	}
	return 0;
    case ' ':
	if (wild_mode) {
	    if (defsel == 10)
		wild_page += 10;
	    else
		wild_page = 0;
	    wildcard();
	    return 1;
	} else if (more_pg) {
	    j = pg_idx;
	    goto next_pg;
	} else if (seltab[sel1st_i][0]) {
	    putstr_inp((u_char *) & seltab[sel1st_i]);	/* select 1st */
	    return 1;
	}
	if (ci == 0) {
	    if (last_full) {
		last_full = 0;
		return 1;
	    }
	    return 0;
	}
	last_full = 0;
	spc_pressed = 1;
	for (i = 0; i < 5; i++)
	    if (inch[i] > 60) {
		wild_page = 0;
		wild_mode = 1;
		wildcard();
		return 1;
	    }
	break;
    case '?':
    case '*':
	if (ci < cur_inmd->MaxPress) {
	    inkey = cur_inmd->keymap[key];
	    inch[ci++] = inkey;
	    DispInArea();
	    if (ci == cur_inmd->MaxPress) {
		wild_page = 0;
		wild_mode = 1;
		wildcard();
	    }
	    return 1;
	}
	return 0;
    default:
	if (is_kp_num(key))
	    key &= 0xff;
	if (key < 32 || key > 0x7e) {
	    if (seltab[sel1st_i][0])
		putstr_inp(seltab[sel1st_i]);	/* select 1st */
	    return 0;
	}
	iselkey = is_selkey(key);
	if (iselkey && !ci)
	    return 0;
	if (!iselkey && seltab[sel1st_i][0] && key != 8 && !wild_mode) {
	    putstr_inp(seltab[sel1st_i]);	/* select 1st */
	}
	if (wild_mode)
	    goto XXXX;
	inkey = cur_inmd->keymap[key];
	spc_pressed = 0;
	if (inkey >= 1 && ci < cur_inmd->MaxPress) {
	    inch[ci++] = inkey;
	    last_full = 0;
	    if (ci == 1 && cur_inmd->use_quick) {
		int             i;
		for (i = 0; i < 10; i++)
		    memcpy(seltab[i], &cur_inmd->quick1[inkey - 1][i][0], 2);
		defsel = 10;
		DispInArea();
		goto Disp_opt;
	    }
	}
	if (inkey)
	    for (i = 0; i < 5; i++)
		if (inch[i] > 60) {
		    DispInArea();
		    if (ci == cur_inmd->MaxPress) {
			wild_mode = 1;
			wild_page = 0;
			wildcard();
		    }
		    return 1;
		}
	if (iselkey && CurInMethod == 8 && inch[0] == 23 && ci == 3)
	    goto YYYY;
	/*
	 * if (iselkey && defsel && (CurInMethod!=8 || inch[0]!=23 ||ci!=2))
	 * goto XXXX;
	 */
	if (!inkey && iselkey && defsel)
	    goto YYYY;
	if (!inkey && !iselkey)
	    return 0;
    }				/* switch */

    if (ci == 0) {
	ClrSelArea();
	ClrInArea();
	ClrIn();
	return 1;
    }
    DispInArea();
    val = inch[4] | (inch[3] << 6) | (inch[2] << 12) | (inch[1] << 18) | (inch[0] << 24);
    if (!last_idx) {
	s1 = cur_inmd->idx1[inch[0]];
    } else
	s1 = last_idx;
    e1 = cur_inmd->idx1[inch[0] + 1];
    while ((CONVT(cur_inmd->tbl[s1].key) & vmask[ci]) != val &&
	   CONVT(cur_inmd->tbl[s1].key) < val && s1 < e1)
	s1++;

    last_idx = s1;

XXXX:
    if ((CONVT(cur_inmd->tbl[s1].key) & vmask[ci]) != val || (wild_mode && defsel) ||
	(ci == cur_inmd->MaxPress || spc_pressed) && defsel && iselkey) {
YYYY:
	if ((iselkey || wild_mode) && defsel) {
	    int             vv = iselkey - cur_inmd->selkey;
	    if (vv < 0)
		vv = 9;
	    if (seltab[vv][0]) {
		putstr_inp(seltab[vv]);
		return 1;
	    }
	}
	if (iselkey && !defsel)
	    return 0;
	bell();
	if (ci > 0)
	    inch[--ci] = 0;
	last_idx = 0;
	DispInArea();
	return 1;
    }
    if (CurInMethod == 8 && CONVT(cur_inmd->tbl[s1].key) == val && iselkey) {
	spc_pressed = 1;
    }
refill:
    j = s1;
    if (ci < cur_inmd->MaxPress && !spc_pressed) {
	int             shiftb = (4 - ci) * KeyBits;

	defsel = 0;
	bzero(seltab, sizeof(seltab));
	while (CONVT(cur_inmd->tbl[j].key) == val && defsel < 10) {
	    if (cur_inmd->tbl[j].ch[0] >= 0x80)
		b2cpy(seltab[defsel++], cur_inmd->tbl[j].ch);
	    j++;
	}
	if (defsel)
	    qsort(seltab, defsel, MAX_CIN_PHR, qcmp_b5);
	exa_match = defsel - 1;
	while ((CONVT(cur_inmd->tbl[j].key) & vmask[ci]) == val && j < e1) {
	    int             fff = cur_inmd->keycol[(CONVT(cur_inmd->tbl[j].key) >> shiftb) &
						   (u_long) 0x3f];
	    if (!(seltab[fff][0]) ||
		(memcmp(seltab[fff], cur_inmd->tbl[j].ch, 2) > 0 && fff > exa_match)) {
		if (cur_inmd->tbl[j].ch[0] >= 0x80) {
		    b2cpy(seltab[fff], cur_inmd->tbl[j].ch);
		    defsel++;
		}
	    }
	    j++;
	}
    } else {
next_pg:
	defsel = more_pg = 0;
	bzero(seltab, sizeof(seltab));
	/*
	 * printf("** pg_idx:%d j:%d  e1:%d val:%x\n", pg_idx, j, e1, val);
	 */
	while (CONVT(cur_inmd->tbl[j].key) == val &&
	       defsel < cur_inmd->M_DUP_SEL && j < e1) {
	    if (cur_inmd->tbl[j].ch[0] < 0x80)
		load_phr(j++, seltab[defsel++]);
	    else
		b2cpy(&seltab[defsel++], cur_inmd->tbl[j++].ch);
	}
	if (j < e1 && CONVT(cur_inmd->tbl[j].key) == val &&
	    defsel == cur_inmd->M_DUP_SEL) {
	    pg_idx = j;
	    more_pg = 1;
	    m_pg_mode = 1;
	} else if (m_pg_mode) {
	    pg_idx = s1;
	    more_pg = 1;
	}
	if (defsel == 1 && !more_pg) {
	    if (ci == cur_inmd->MaxPress)
		last_full = 1;
	    putstr_inp(seltab[0]);
	    return 1;
	} else if (!defsel) {
	    bell();
	    spc_pressed = 0;
	    goto refill;
	} else if (!more_pg)
	    bell();
    }

Disp_opt:

    ClrSelArea();
    sel1st_i = 15;
    for (i = 0; i < 10; i++)
	if (seltab[i][2])
	    break;
    if (i >= 10) {
	gotox(SelAreaX + 37);
	for (i = 9; i >= 0; i--) {
	    int             k;
	    if (seltab[i][0]) {
		int             new_x;
		if (cur_inmd->sel1st && (spc_pressed || ci == cur_inmd->MaxPress)) {
		    for (k = 0; cur_inmd->sel1st[k]; k += 2)
			if (!memcmp(&cur_inmd->sel1st[k], seltab[i], 2)) {
			    set_att(InAreaColor);
			    sel1st_i = i;
			    break;
			}
		    if (sel1st_i == 15 && !i && !more_pg) {
			set_att(InAreaColor);
			sel1st_i = i;
		    }
		} else {
		    if ((spc_pressed || ci == cur_inmd->MaxPress) && !i && !more_pg) {
			set_att(InAreaColor);
			sel1st_i = i;
		    }
		}
		xprintf("%d", (i + 1) % 10);
		set_att(NormalColor);
		xprintf("%s", seltab[i]);
		cursor_x -= 7;
	    } else
		cursor_x -= 4;
	}
    } else {
	gotox(SelAreaX);
	for (i = 0; i < 10; i++) {
	    if (seltab[i][0]) {
		xprintf("%d", (i + 1) % 10);
		set_att(NormalColor);
		xprintf("%s ", seltab[i]);
	    }
	}
    }

    return 1;

}
