#include    <stdio.h>
#include    <stdlib.h>
#include    <ctype.h>
#include    <jctype.h>
#include    <string.h>
#include    <malloc.h>
#include    <dos.h>
#include    "defs.h"
#include    "key.h"

#define	KEY_BUF_SIZ	256
#define	KEY_BUF_MSK	255

static	char	matrix[16];
static	int	key_len=0;
static	int	key_top=0;
static	int	key_pos=0;
static	struct {
	int	ch;
	int	ec;
} key_buf[KEY_BUF_SIZ];

static	struct {
	int	mask;
	int	ec;
	int	ch;
} assign[]={
		{ 0xFF00, 0x4800, 0x8000 },	/* 挿入 */
		{ 0xFF00, 0x4B00, 0x007F },	/* 削除 */
		{ 0xFF04, 0x4E00, 0x000B },	/* HOME */
		{ 0xFF04, 0x4E04, 0x000C },	/* CLS  */
		{ 0xFF00, 0x7300, 0x000D },	/* 実行 */
		{ 0xFF00, 0x7200, 0x001B },	/* 取消 */
		{ 0xFF00, 0x5D00, 0x8001 },	/* PF1  */
		{ 0xFF00, 0x5E00, 0x8002 },	/* PF2  */
		{ 0xFF00, 0x5F00, 0x8003 },	/* PF3  */
		{ 0xFF00, 0x6000, 0x8004 },	/* PF4  */
		{ 0xFF00, 0x6100, 0x8005 },	/* PF5  */
		{ 0xFF00, 0x6200, 0x8006 },	/* PF6  */
		{ 0xFF00, 0x6300, 0x8007 },	/* PF7  */
		{ 0xFF00, 0x6400, 0x8008 },	/* PF8  */
		{ 0xFF00, 0x6500, 0x8009 },	/* PF9  */
		{ 0xFF00, 0x6600, 0x800A },	/* PF10 */
		{ 0xFF00, 0x6900, 0x800B },	/* PF11 */
		{ 0xFF00, 0x5B00, 0x800C },	/* PF12 */
		{ 0xFF00, 0x7400, 0x800D },	/* PF13 */
		{ 0xFF00, 0x7500, 0x800E },	/* PF14 */
		{ 0xFF00, 0x7600, 0x800F },	/* PF15 */
		{ 0xFF00, 0x7700, 0x8010 },	/* PF16 */
		{ 0xFF00, 0x7800, 0x8011 },	/* PF17 */
		{ 0xFF00, 0x7900, 0x8012 },	/* PF18 */
		{ 0xFF00, 0x7A00, 0x8013 },	/* PF19 */
		{ 0xFF00, 0x7B00, 0x8014 },	/* PF20 */
		{ 0xFF10, 0x4D10, 0x8017 },	/* ^UP   */
		{ 0xFF10, 0x5010, 0x8018 },	/* ^DOWN */
		{ 0xFF10, 0x4F10, 0x8019 },	/* ^LEFT */
		{ 0xFF10, 0x5110, 0x801A },	/* ^RIGHT*/
		{ 0x0000, 0x0000, 0x0000 },
	};

static	int	KYB_read(int sw,int *ec)
{
    union REGS regs;

    regs.h.ah = 0x09;
    regs.h.al = sw;
    int86(0x90,&regs,&regs);
    *ec = regs.x.bx;
    return regs.x.dx;
}
static	int	KYB_inpchk(int *ch,int *ec)
{
    union REGS regs;

    regs.h.ah = 0x07;
    int86(0x90,&regs,&regs);
    *ec = regs.x.bx;
    *ch = regs.x.dx;
    return regs.h.al;
}
static	int	KYB_matrix(char *tmp)
{
    union REGS regs;
    struct SREGS seg;
    union {
	char far *p;
	short	 s[2];
    } pp;

    pp.p = (char far *)(tmp);
    regs.h.ah = 0x0A;
    regs.x.di = pp.s[0];
    seg.ds    = pp.s[1];
    int86x(0x90, &regs, &regs, &seg);
    return regs.h.ah;
}
static	void	NEW_bufset(int ch,int ec)
{
    if ( key_len >= KEY_BUF_SIZ )
	return;
    key_buf[key_top].ch = ch;
    key_buf[key_top].ec = ec;
    key_top++;
    key_top &= KEY_BUF_MSK;
    key_len++;
}
static	int	NEW_inpchk(int *ch,int *ec)
{
    int i, n;
    int ch2, ec2;
    static struct {
	    int		on;
	    int		ec;
	    int		st;
	} ext_tab[] = {
		{ 0x8015, 0x7D00, FALSE },		/* COPY  */
		{ 0x8016, 0x7C00, FALSE },		/* BREAK */
		{ 0x0000, 0x0000, 0 },
	};

    KYB_matrix(matrix);
    for ( i = 0 ; (n = ext_tab[i].ec >> 8) != 0 ; i++ ) {
	if ( (matrix[n / 8] & (1 << (n % 8))) != 0 ) {
	    if ( ext_tab[i].st == FALSE )
		NEW_bufset(ext_tab[i].on, ext_tab[i].ec);
	    ext_tab[i].st = TRUE;
	} else
	    ext_tab[i].st = FALSE;
    }

    if ( key_len > 0 ) {
	*ch = key_buf[key_pos].ch;
	*ec = key_buf[key_pos].ec;
	return key_len;
    }

    *ch = KYB_read(1, ec);
    if ( (*ec & 0xFF00) == 0xFF00 )
	return 0;

    for ( i = 0 ; assign[i].ec != 0 ; i++ ) {
	if ( (*ec & assign[i].mask) == assign[i].ec ) {
	    while ( KYB_inpchk(&ch2, &ec2) > 0 && *ch != ch2 && *ec == ec2 )
		ch2 = KYB_read(0, &ec2);
	    *ch = assign[i].ch;
	    NEW_bufset(assign[i].ch, *ec);
	    return key_len;
	}
    }

    NEW_bufset(*ch, *ec);
    return key_len;
}
static	int	NEW_read(int sw, int *ec)
{
    int     ch=0xFFFF;

    do {
	if ( NEW_inpchk(&ch,ec) > 0 ) {
	    key_pos++;
	    key_pos &= KEY_BUF_MSK;
	    key_len--;
	    break;
        }
    } while ( sw == 0 );

    return ch;
}
int	isank(ch)
int	ch;
{
    return ( !iskanji(ch) && ch >= ' ' && ch != 0x7F );
}
void	tsleep(tic)
int	tic;
{
    union REGS regs;

    regs.x.cx = tic * 100;
    int86(0xFD,&regs,&regs);
}

/**********************
^A=LEFT_CUR
^B=RIGHT_CUR
^C=LEFT_NODE
^D=RIGHT_NODE
PF1=
RIGHT="any strings"
***********************/

static	int	any_ent = 0;
static	int	any_max = 0;
static	char	**any_vct;
static	char	*any_str = NULL;
static struct {
	int	code;
	char	*key;
    } key_name[] = {
	{ 0x8000,	"INS" },
	{ 0x8001,	"PF1" },
	{ 0x8002,	"PF2" },
	{ 0x8003,	"PF3" },
	{ 0x8004,	"PF4" },
	{ 0x8005,	"PF5" },
	{ 0x8006,	"PF6" },
	{ 0x8007,	"PF7" },
	{ 0x8008,	"PF8" },
	{ 0x8009,	"PF9" },
	{ 0x800A,	"PF10" },
	{ 0x800B,	"PF11" },
	{ 0x800C,	"PF12" },
	{ 0x800D,	"PF13" },
	{ 0x800E,	"PF14" },
	{ 0x800F,	"PF15" },
	{ 0x8010,	"PF16" },
	{ 0x8011,	"PF17" },
	{ 0x8012,	"PF18" },
	{ 0x8013,	"PF19" },
	{ 0x8014,	"PF20" },
	{ 0x8015,	"COPY" },
	{ 0x8016,	"BREAK" },
	{ 0x8017,	"^UP" },
	{ 0x8018,	"^DOWN" },
	{ 0x8019,	"^LEFT" },
	{ 0x801A,	"^RIGHT" },
	{ 0x001B,	"ESC" },
	{ 0x001C,	"LEFT" },
	{ 0x001D,	"RIGHT" },
	{ 0x001E,	"UP" },
	{ 0x001F,	"DOWN" },
	{ 0x007F,	"DEL" },
	{ 0x0000,	NULL }
    };
static struct {
	int	code;
	char	*str;
    } key_man[] = {
		      /* 12345678901234567890 */
	{ K_END_OF,	"End of Program" },
	{ K_BACK_SPC,	"Edit back space" },
	{ K_DEL_CHAR,	"Edit delete char" },
	{ K_LEFT_CUR,	"Edit left cursol" },
	{ K_RIGHT_CUR,	"Edit right cursol" },
	{ K_CUT_BUFF,	"Edit cut line" },
	{ K_DEL_LINE,	"Edit delete line" },
	{ K_UNDO_LINE,	"Edit paste line" },
	{ K_HIS_LINE,	"Edit undo char" },
	{ K_END_LINE,	"Edit execute" },

	{ K_WIND_CNG,	"Wind change" },
	{ K_UP_NODE,	"Wind up cursol" },
	{ K_DOWN_NODE,	"Wind down cursol" },
	{ K_GETS_WIND,	"Edit get act wind" },
	{ K_GETS_SRC,	"Edit get src wind" },
	{ K_GETS_DIS,	"Edit get dis wind" },
	{ K_COPY_WIND,	"Copy src to dis" },
	{ K_DIR_MENU,	"Wind chdir menu" },
	{ K_SCREEN_FLUSH,"Screen flush" },
	{ K_MARK_FILE,	"Wind mark file" },
	{ K_INIT_WIND,	"Wind init" },
	{ K_MARK_ALL,	"Wind mark all file" },
	{ K_SKIP_DOC,	"Wind skip doc file" },
	{ K_ABORT,	"Abort" },

	{ K_CONSOLE,	"DOS Console" },
	{ K_HISTORY,	"Edit history" },
	{ K_HIS_DIR,	"Wind chdir history" },
	{ K_TREE_DIR,	"Wind chdir tree" },
	{ K_SORT_MODE,	"Wind disp sort mode" },
	{ K_HELP,	"Help screen" },
	{ K_DRV_MENU,	"Wind Drive menu" },

	{ K_UP_MARK,	"Wind mark up node" },
	{ K_DOWN_MARK,	"Wind mark down node" },

	{ 0, NULL },
    };
static struct {
	int	code;
	char	*str;
    } func_name[] = {
	{ K_END_OF,	"END_OF" },
	{ K_BACK_SPC,	"BACK_SPC" },
	{ K_DEL_CHAR,	"DEL_CHAR" },
	{ K_LEFT_CUR,	"LEFT_CUR" },
	{ K_RIGHT_CUR,	"RIGHT_CUR" },
	{ K_CUT_BUFF,	"CUT_BUFF" },
	{ K_DEL_LINE,	"DEL_LINE" },
	{ K_UNDO_LINE,	"UNDO_LINE" },
	{ K_HIS_LINE,	"HIS_LINE" },
	{ K_END_LINE,	"END_LINE" },

	{ K_WIND_CNG,	"WIND_CNG" },
	{ K_UP_NODE,	"UP_NODE" },
	{ K_DOWN_NODE,	"DOWN_NODE" },
	{ K_GETS_WIND,	"GETS_WIND" },
	{ K_GETS_SRC,	"GETS_SRC" },
	{ K_GETS_DIS,	"GETS_DIS" },
	{ K_COPY_WIND,	"COPY_WIND" },
	{ K_DIR_MENU,	"DIR_MENU" },
	{ K_SCREEN_FLUSH,"SCREEN_FLUSH" },
	{ K_MARK_FILE,	"MARK_FILE" },
	{ K_INIT_WIND,	"INIT_WIND" },
	{ K_MARK_ALL,	"MARK_ALL" },
	{ K_SKIP_DOC,	"SKIP_DOC" },
	{ K_ABORT,	"ABORT" },

	{ K_CONSOLE,	"CONSOLE" },
	{ K_HISTORY,	"HISTORY" },
	{ K_HIS_DIR,	"HIS_DIR" },
	{ K_TREE_DIR,	"TREE_DIR" },
	{ K_SORT_MODE,	"SORT_MODE" },
	{ K_HELP,	"HELP" },
	{ K_DRV_MENU,	"DRV_MENU" },

	{ K_UP_MARK,	"UP_MARK" },
	{ K_DOWN_MARK,	"DOWN_MARK" },

	{ 0x0000,	NULL }
    };

#define	CTRL_MAP_LEN	33

static	int	ctrl_map[] = {
	K_CONSOLE,		/* ^@ */
	K_HIS_LINE,		/* ^A */
	K_CUT_BUFF,		/* ^B */
	K_INIT_WIND,		/* ^C */
	K_GETS_DIS,		/* ^D */
	EOF,			/* ^E */
	EOF,			/* ^F */
	EOF,			/* ^G */
	K_BACK_SPC,		/* ^H */
	K_WIND_CNG,		/* ^I */
	EOF,			/* ^J */
	EOF,			/* ^K */
	K_SCREEN_FLUSH,		/* ^L */
	K_END_LINE,		/* ^M */
	K_SKIP_DOC,		/* ^N */
	EOF,			/* ^O */
	K_HISTORY,		/* ^P */
	K_MARK_FILE,		/* ^Q */
	K_SORT_MODE,		/* ^R */
	K_GETS_SRC,		/* ^S */
	EOF,			/* ^T */
	K_UNDO_LINE,		/* ^U */
	K_HELP,			/* ^V */
	K_MARK_ALL,		/* ^W */
	K_DEL_LINE,		/* ^X */
	K_DEL_LINE,		/* ^Y */
	K_GETS_WIND,		/* ^Z */
	K_ABORT,		/* ESC */
	K_LEFT_CUR,		/* LEFT */
	K_RIGHT_CUR,		/* RIGHT */
	K_UP_NODE,		/* UP */
	K_DOWN_NODE,		/* DOWN */
	K_DEL_CHAR,		/* DEL */
    };

#define	PF_MAP_LEN	27

static	int	pf_map[] = {
	EOF,			/* INS */
	K_COPY_WIND,		/* PF1 */
	K_HIS_DIR,		/* PF2 */
	K_DIR_MENU,		/* PF3 */
	K_TREE_DIR,		/* PF4 */
	K_DRV_MENU,		/* PF5 */
	K_MARK_FILE,		/* PF6 */
	K_MARK_ALL,		/* PF7 */
	K_SKIP_DOC,		/* PF8 */
	K_CONSOLE,		/* PF9 */
	K_END_OF,		/* PF10 */
	EOF,			/* PF11 */
	EOF,			/* PF12 */
	EOF,			/* PF13 */
	EOF,			/* PF14 */
	EOF,			/* PF15 */
	EOF,			/* PF16 */
	EOF,			/* PF17 */
	EOF,			/* PF18 */
	EOF,			/* PF19 */
	EOF,			/* PF20 */
	K_COPY_WIND,		/* COPY */
	K_ABORT,		/* BREAK*/
	EOF,			/* ^UP   */
	EOF,			/* ^DOWN */
	EOF,			/* ^LEFT */
	EOF,			/* ^RIGHT*/
    };

static	char	*strany(char *str)
{
    int n = 0;
    int c, i;
    static char tmp[LINSIZ + 2];

    while ( n < LINSIZ && *str != '\0' ) {
	if ( iskan(str) ) {
	    tmp[n++] = *(str++);
	    tmp[n++] = *(str++);
	} else if ( *str == '\\' ) {
	    str++;
	    switch(*(str++)) {
	    case 'r':
	    case 'n': tmp[n++] = '\r'; break;
	    case 'b': tmp[n++] = '\b'; break;
	    case 'f': tmp[n++] = '\f'; break;
	    case 't': tmp[n++] = '\t'; break;
	    case 'x':
		for ( i = c = 0 ; i < 2 && isxdigit(*str) ; i++ ) {
		    if ( isdigit(*str) )
			c = c * 16 + (*(str++) - '0');
		    else {
			c = c * 16 + (toupper(*str) - 'A' + 10);
			str++;
		    }
		}
		tmp[n++] = c;
		break;
	    default:
		if ( *str >= '0' && *str <= '7' ) {
		    for ( i = c = 0 ; i < 3 &&
				*str >= '0' && *str <= '7' ; i++ )
			c = c * 8 + (*(str++) - '0');
		    tmp[n++] = c;
		} else
		    tmp[n++] = *(str - 1);
		break;
	    }
	} else {
	    tmp[n++] = *(str++);
	}
    }
    tmp[n] = '\0';
    return tmp;
}
static	int	enc_key(char *str)
{
    int n;

    for ( n = 0 ; key_name[n].key != NULL ; n++ ) {
	if ( strcmp(str, key_name[n].key) == 0 )
	    return key_name[n].code;
    }

    if ( *str == '^' )
	return toupper((str[1]) - '@');
    else if ( strncmp(str, "CTRL+", 5) == 0 )
	return toupper((str[5]) - '@');

    return EOF;
}
static	int	enc_func(char *str)
{
    int n;
    char *p;

    for ( n = 0 ; func_name[n].str != NULL ; n++ ) {
	if ( strcmp(str, func_name[n].str) == 0 )
	    return func_name[n].code;
    }

    if ( *(str++) == '"' ) {
	if ( (p = strrchr(str, '"')) != NULL )
	    *p = '\0';
	if ( any_ent >= any_max ) {
	    any_max += 8;
	    if ( any_vct == (char **)NULL )
		any_vct = (char **)malloc(sizeof(char *) * any_max);
	    else
		any_vct = (char **)realloc(any_vct, sizeof(char *) * any_max);

	    if ( any_vct == (char **)NULL ) {
		fprintf(stderr, "key strings malloc error\n");
		exit(1);
	    }
	    any_vct[any_ent] = strdup(str);
	}
	return (K_STRING + (any_ent++));
    }

    return EOF;
}
static	char	*key_str(int code)
{
    int n;
    static char tmp[8];

    for ( n = 0 ; key_name[n].key != NULL ; n++ ) {
	if ( code == key_name[n].code )
	    return key_name[n].key;
    }
    sprintf(tmp, "^%c", code + '@');
    return tmp;
}
static	char	*key_cmd(int code)
{
    int n;

    for ( n = 0 ; key_man[n].str != NULL ; n++ ) {
	if ( code == key_man[n].code )
	    return key_man[n].str;
    }

    if ( code >= K_STRING )
	return any_vct[code - K_STRING];

    return "";
}
void	key_map()
{
    int n, c;
    int x = 0, y = 2;

    for ( n = 0 ; n < CTRL_MAP_LEN ; n++ ) {	/* ctrl_map */
	c = (n == 32 ? 0x7F : n);
	LOCATE(x, y);
	FPUTS("%-5s %-20.20s", key_str(c), key_cmd(ctrl_map[n]));
	if ( ++y > (SCR_Y - 2) ) {
	    y = 2;
	    x += 26;
	}
    }
    for ( n = 0 ; n < PF_MAP_LEN ; n++ ) {	/* pf_map */
	c = 0x8000 | n;
	LOCATE(x, y);
	FPUTS("%-5s %-20.20s", key_str(c), key_cmd(pf_map[n]));
	if ( ++y > (SCR_Y - 2) ) {
	    y = 2;
	    x += 26;
	}
    }
}
int     REALCH()
{
    int     ec;

    if ( any_str != NULL ) {
	if ( *any_str != '\0' )
	    return (*(any_str++) & 0x00FF);
	any_str = NULL;
    }

    return NEW_read(0, &ec);
}
int	GETCH()
{
    int ch;

LOOP:
    do {
        ch = REALCH();
        if ( (ch & 0xFF00) != 0 ) {
	    if ( (ch & 0xFF00) == 0x8000 && (ch & 0x00FF) < PF_MAP_LEN )
	        ch = pf_map[ch & 0x00FF];
	    else
		ch = EOF;
        } else if ( ch < ' ' )
	    ch = ctrl_map[ch];
        else if ( ch == 0x007F )
	    ch = ctrl_map[32];
    } while ( ch == EOF );

    if ( ch >= K_STRING ) {
	any_str = strany(any_vct[ch - K_STRING]);
	goto LOOP;
    }

    return ch;
}
void    UNGETCH(int ch)
{
    if ( any_str != NULL )
	any_str--;
    else
	NEW_bufset(ch, 0);
}
int	KBHIT()
{
    int     ch,ec;

    if ( any_str != NULL && *any_str != '\0' )
	return 1;
    else
        return NEW_inpchk(&ch, &ec);
}

void	key_defs(FILE *fp, char *tmp)
{
    int  ch;
    char *p;

    while ( fgets(tmp, LINSIZ, fp) != NULL ) {
	if ( (p = strchr(tmp, '\n')) != NULL )
	    *p = '\0';
        if ( strcmp(tmp, "#end") == 0 )
            break;
	if ( (p = strchr(tmp, '=')) == NULL )
	    continue;
	*(p++) = '\0';
	while ( isspace(*p) )
	    p++;
	if ( (ch = enc_key(tmp)) == EOF )
	    continue;

	if ( (ch & 0xFF00) == 0 && ch < ' ' )
	    ctrl_map[ch] = enc_func(p);
	else if ( ch == 0x007F )
	    ctrl_map[32] = enc_func(p);
	else if ( (ch & 0x00FF) < PF_MAP_LEN )
	    pf_map[ch & 0x00FF] = enc_func(p);
    }
}
void	KEYINIT()
{
}
void	KEYEND()
{
}
void	setkey()
{
}
