#include    <stdio.h>
#include    <stdlib.h>
#include    <malloc.h>
#include    <string.h>
#include    <ctype.h>

#ifdef	MSDOS
#include    <jctype.h>
#endif

#include    "defs.h"
#include    "key.h"

void	wind(int sx, int sy, int wd, int ht)
{
    int n, i;

#ifdef	MSDOS
    sx -= 1;
    sy -= 1;

    LOCATE(sx, sy++);
    PUTANK(0x9C);
    for ( n = 0 ; n < wd ; n++ )
	PUTANK(0x95);
    PUTANK(0x9D);

    for ( i = 0 ; i < ht ; i++ ) {
	LOCATE(sx, sy++);
	PUTANK(0x96);
	for ( n = 0 ; n < wd ; n++ )
	    PUTANK(' ');
	PUTANK(0x96);
    }

    LOCATE(sx, sy++);
    PUTANK(0x9E);
    for ( n = 0 ; n < wd ; n++ )
	PUTANK(0x95);
    PUTANK(0x9F);
#else
    sx -= 2;
    sy -= 1;

    LOCATE(sx, sy++);
    PUTS("„¡");
    for ( n = 0 ; n < wd ; n += 2 )
	PUTS("„Ÿ");
    PUTS("„¢");

    for ( i = 0 ; i < ht ; i++ ) {
	LOCATE(sx, sy++);
	PUTS("„ ");
	for ( n = 0 ; n < wd ; n += 2 )
	    PUTS("  ");
	PUTS("„ ");
    }

    LOCATE(sx, sy++);
    PUTS("„¤");
    for ( n = 0 ; n < wd ; n += 2 )
	PUTS("„Ÿ");
    PUTS("„£");
#endif
}
int	hash_menu(int x, int y, int len, char *arg[])
{
    int i, n;
    int ent = 0;
    int max = 16;
    int ext = 1;
    char **av;
    short *nv;
    char tmp[36];
    char *p;

    if ( (av = (char **)malloc(sizeof(char *) * max)) == NULL )
        return ERR;

    if ( len >= 32 )
	len = 32;

LOOP:
    for ( n = 0 ; arg[n] != NULL ; n++ ) {
	strncpy(tmp, arg[n], len);
	tmp[len] = '\0';
	for ( p = tmp ; *p != '\0' ; ) {
	    if ( iskanji(p[0]) ) {
		if ( !iskanji2(p[1]) )
		    break;
		p += 2;
	    } else
		p++;
	}
	*p = '\0';

	for ( i = 0 ; i < ent ; i++ ) {
	    if ( strcmp(av[i], tmp) == 0 )
		break;
	}
	if ( ent > 0 && i < ent )
	    continue;

	av[ent++] = strdup(tmp);
        if ( ent >= max ) {
            max += 32;
            if ( (av = (char **)realloc(av, sizeof(char *) * max)) == NULL )
                return ERR;
        }
    }
    av[ent] = NULL;

    if ( n == ent ) {
        for ( n = 0 ; n < ent ; n++ )
            free(av[n]);
	free(av);
	return menu(x, y, 0, arg);

    } else if ( ent <= 1 ) {
        for ( n = 0 ; n < ent ; n++ )
            free(av[n]);
	ent = 0;
	if ( len >= 16 )
	    return menu(x, y, 0, arg);
	len += 2;
	goto LOOP;

    } else if ( (n = menu(x, y, 0, av)) < 0 ) {
        for ( n = 0 ; n < ent ; n++ )
            free(av[n]);
	free(av);
	return ERR;
    }

    strcpy(tmp, av[n]);
    len = strlen(tmp);

    for ( n = 0 ; n < ent ; n++ )
        free(av[n]);
    ent = 0;

    if ( (nv = (short *)malloc(sizeof(short) * max)) == NULL )
        return ERR;

    for ( n = 0 ; arg[n] != NULL ; n++ ) {
	if ( strncmp(arg[n], tmp, len) != 0 )
	    continue;
	nv[ent]   = n;
	av[ent++] = arg[n];
        if ( ent >= max ) {
            max += 32;
            if ( (av = (char **)realloc(av, sizeof(char *) * max)) == NULL ||
                 (nv = (short *)realloc(nv, sizeof(short) * max)) == NULL )
                return ERR;
        }
    }
    av[ent] = NULL;

    if ( ent >= 20 ) {
	if ( (n = hash_menu(x + 4, y + 1, len + 2, av)) >= 0 )
	    n = nv[n];
    } else if ( (n = menu(x + 4, y + 1 , 0, av)) >= 0 )
	n = nv[n];

    free(nv);
    free(av);

    return n;
}
int     menu(int x, int y, int no, char *arg[])
{
    int i, n, r, ch;
    int len, max, mid, btm;
    int od;
    int rc = FALSE;
    char form[64];
    static int top = 0;

    for ( len = max = 0 ; arg[max] != NULL ; max++ ) {
	if ( (i = strlen(arg[max])) > len )
	    len = i;
    }

    if ( (y + max + 1) >= SCR_Y )
	btm = SCR_Y - y - 1;
    else
	btm = max;

    if ( (x + len) > (SCR_X - 2) ) {
	if ( (x = SCR_X - 2 - len) < 2 )
	    x = 2;
	if ( (x + len) > (SCR_X - 2) )
	    len = (SCR_X - 2 - x);
    }

    if ( max > btm && (len * 2 + 2) < (SCR_X - 4) ) {
	mid = (max + btm - 1) / btm;
	if ( ((len + 1) * mid) > (SCR_X - 4) )
	    mid = (SCR_X - 4) / (len + 1);
	if ( (x + (len + 1) * mid) > (SCR_X - 2) )
	    x = SCR_X - 2 - (len + 1) * mid;
    } else
	mid = 1;

    sprintf(form, "%%-%d.%ds", len, len);

    CUROFF();
    ACTCOL();
    wind(x, y, (mid > 1 ? ((len + 1) * mid) : len), btm);
    STDCOL();

    if ( no <= 0 )
	no = top = 0;
    else if ( max == btm || top >= max )
	top = 0;
    else
	top = (top / btm) * btm;
    od = (-1);

    while ( rc == FALSE ) {

        if ( od != no ) {
	    if ( od < 0 || no < top || no >= (top + (btm * mid)) ) {
		while ( no < top )
		    top -= btm;
		while ( no >= (top + (btm * mid)) )
		    top += btm;
		for ( n = 0 ; n < mid ; n++ ) {
		    for ( i = 0 ; i < btm ; i++ ) {
			LOCATE(x + (len + 1) * n, y + i);
			r = i + btm * n + top;
			if ( r == no )
			    REVCOL();
			if ( r >= max )
			    REPCHR(' ', len);
			else
			    FPUTS(form, arg[r]);
			if ( r == no )
			    NOMCOL();
		    }
		}
	    } else {
                LOCATE(x + (len + 1) * ((od - top) / btm),
		       y + (od - top) % btm);
                FPUTS(form, arg[od]);
                LOCATE(x + (len + 1) * ((no - top) / btm),
		       y + (no - top) % btm);
                REVCOL();
                FPUTS(form, arg[no]);
                NOMCOL();
	    }
            LOCATE(x + (len + 1) * ((no - top) / btm) + len,
		   y + (no - top) % btm);
            FLUSH();
            od = no;
        }

        ch = GETCH();

        switch(ch) {
	case K_UP_NODE:
	case K_BACK_SPC:
	case 0x1E:
	case 0x08:
            if ( --no < 0 )
                no = max - 1;
	    break;

	case K_DOWN_NODE:
	case 0x1F:
	case 0x20:
            if ( ++no >= max )
                no = 0;
	    break;

	case K_RIGHT_CUR:
	case 0x1D:
	    if ( mid < 2 )
		break;
	    if ( (no -= btm) < 0 ) {
		if ( (no = (max / btm) * btm + (no + btm)) >= max )
		    no -= btm;
	    }
	    break;

	case K_LEFT_CUR:
	case 0x1C:
	    if ( mid < 2 )
		break;
	    if ( (no += btm) >= max )
		no %= btm;
	    break;

	case K_END_LINE:
        case 0x0D:
            rc = TRUE;
	    break;

	case K_ABORT:
        case 0x1B:
            rc = ERR;
	    break;

        default:
	    if ( (ch & 0xFF00) != 0 )
		rc = ERR;
	    else {
                for ( i = 0 ; i < max ; i++ ) {
                    if ( toupper(ch) == *arg[i] ) {
                        no = i;
                        rc = TRUE;
		    }
                }
            }
	    break;
        }
    }

    CURON();
    return (rc == TRUE ? no : (-1));
}
