/*
 *   Copyright (c) 2002 David Parsons. All rights reserved.
 *   
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *  3. All advertising materials mentioning features or use of this
 *     software must display the following acknowledgement:
 *     
 *   This product includes software developed by David Parsons
 *   (orc@pell.chi.il.us)
 *
 *  4. My name may not be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *  THIS SOFTWARE IS PROVIDED BY DAVID PARSONS ``AS IS'' AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 *  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID
 *  PARSONS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 *  THE POSSIBILITY OF SUCH DAMAGE.
 */
/*
 * keypad() for BSD curses in libc 4.10.0 (ripped out of ndialog)
 */
#include "curses.h"
#include "curses.ext"

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <malloc.h>

static struct {
    char *key;
    char **cap;
    int name;
} keywewant[] = {
    {    0, &K0, KEY_F0 },
    {    0, &K1, KEY_F1 },
    {    0, &K2, KEY_F2 },
    {    0, &K3, KEY_F3 },
    {    0, &K4, KEY_F4 },
    {    0, &K5, KEY_F5 },
    {    0, &K6, KEY_F6 },
    {    0, &K7, KEY_F7 },
    {    0, &K8, KEY_F8 },
    {    0, &K9, KEY_F9 },
    {    0, &KD, KEY_DOWN },
    {    0, &KL, KEY_LEFT },
    {    0, &KR, KEY_RIGHT },
    {    0, &KU, KEY_UP },
    { "kN", 0,   KEY_NPAGE },
    { "kP", 0,   KEY_PPAGE },
};
#define NR_K	(sizeof keywewant/sizeof keywewant[0])

struct keymac {
    enum { FIN=0, TERM=1, MORE=2 } term;
    char c;
    union {
	struct keymac *next;
	int key;
    } u;
} ;

static int    keypad_on = 0;
static struct keymac *function_keys = 0;
static int    keylen_max = 0;
static int    keylen_min = 9999;
#define KHBITS	(8*sizeof(long))
static long   key_hash[256/KHBITS];

/*
 * addmac() adds a function key to the macros table
 */
static struct keymac *
addmac(int d, struct keymac *p, char *v, int key)
{
    struct keymac *r;
    int sz = 0;
    unsigned char c;

    if (v == 0 || *v == 0) return p;

# ifdef DEBUG
    fprintf(outf, "%*saddmac(%d,%x,%s,%d)\n", d, "", d, p, unctrl(*v), key);
# endif

    if (p) {
	for (r = p; r->term; r++) {
	    if (r->c == *v) {
		if (r->term == MORE)
		    r->u.next = addmac(1+d, r->u.next, 1+v, key);
		return p;
	    }
	    sz++;
	}
    }
    else {
	r = p = calloc(3, sizeof p[0]);
	sz = 0;
    }
    
    if (d == 1) {
	c = *v;
	key_hash[c/KHBITS] |= 1<<(c%KHBITS);
    }

    r->c = *v;
    if (v[1]) {
	r->term = MORE;
	r->u.next = addmac(1+d, 0, 1+v, key);
    }
    else {
	r->term = TERM;
	r->u.key = key;
	if (d < keylen_min) keylen_min = d;
	if (d > keylen_max) keylen_max = d;
    }
    ++r;
    r->term = 0;
    return realloc(p, (sz+3)*sizeof p[0]);
}


int
keypad(WINDOW * win, int on)
{
#if DEBUG
    fprintf(outf, "keypad(%x, %d)\n", win, on);
#endif
    if (on) {
	setbuf(stdin, 0);
	if (KS) _puts(KS);
    }
    else if (KE)
	_puts(KE);
    keypad_on = on;
    return 1;
}


void
__macros()
{
    int i;

    function_keys = 0;	/* FIX ME */
    for (i=0; i < NR_K; i++) {
	function_keys = addmac(1,function_keys,
				 keywewant[i].cap ? *(keywewant[i].cap)
						  : getcap(keywewant[i].key),
				 keywewant[i].name);
    }
}


int
__llgetch()
{
    int c, ct, rc, c2;
    fd_set fds;
    struct timeval tick;
    struct keymac *p;

again:
    c = getchar();

    if ( keypad_on && (key_hash[c/KHBITS] & 1<<(c%KHBITS)) ) {
	/* possible function key */

# ifdef DEBUG
	fprintf(outf, "__llgetch() - function leadin %s\n", unctrl(c));
# endif

	ct = 1;
	for (p = function_keys; p->term && p->c != c; p++)
	    ;
	switch (p->term) {
	case FIN:	goto again;
	case TERM:
# ifdef DEBUG
			fprintf(outf, "__llgetch()[1] - return %d\n"< p->u.key);
# endif
			return p->u.key;
	default:	tick.tv_sec = 0;
			tick.tv_usec = 50000;
			break;
	}
	FD_ZERO(&fds);
	while (1) {
	    FD_SET(0, &fds);

	    rc = select(1, &fds, 0, 0, &tick);

	    if (rc == 0 || !FD_ISSET(0, &fds)) {
# ifdef DEBUG
		fprintf(outf, "__llgetch() timeout\n");
# endif
		break;
	    }

	    ct++;
	    c2 = getchar();
# ifdef DEBUG
	    fprintf(outf, "__llgetch() - next char %s\n", unctrl(c2));
# endif


	    for (p = p->u.next; p->term && p->c != c2; p++)
		;
	    switch (p->term) {
	    case FIN:	goto again;
	    case TERM:
# ifdef DEBUG
			fprintf(outf, "__llgetch() -return %d\n", p->u.key);
# endif
			return p->u.key;
	    default:	tick.tv_sec = 0;
			tick.tv_usec = 25000;
			break;
	    }
	}
    }
    else if (c == erasechar())
	return KEY_BACKSPACE;
    return c;
}
