/*
 *	Video I/O Support for T3X programs under Unix.
 *	Copyright (C) 1996,1998 Nils M. Holm.
 *	See the file LICENSE for conditions of use.
 */

#ifdef VIO_EXT

#include <stdio.h>
#include <stdlib.h>
#include <termcap.h>
#include <sgtty.h>
#include <unistd.h>
#include <sys/time.h>

#define K_HOME	327
#define K_WLFT	371
#define K_LEFT	331
#define K_RGHT	333
#define K_WRGT	372
#define K_END	335
#define K_BKSP	'\b'
#define K_DEL	339
#define K_KILL	127
#define K_INS	338
#define K_CR	'\r'
#define K_UP	328
#define K_DOWN	336
#define K_ESC	'\033'
#define K_PGUP	329
#define K_PGDN	337
#define K_CPGU	388
#define K_CPGD	374
#define K_F1	315
#define K_F2	316
#define K_F3	317
#define K_F4	318
#define K_F5	319
#define K_F6	320
#define K_F7	321
#define K_F8	322
#define K_F9	323
#define K_F10	324


#define AL	0
#define CE	1
#define CL	2
#define CM	3
#define DL	4
#define SE	5
#define SO	6
#define DC	7
#define IC	8

static char	*tc[10][4] = {
	{	"al",	NULL,	"add line"	},
	{	"ce",	NULL,	"erase line"	},
	{	"cl",	NULL,	"erase display"	},
	{	"cm",	NULL,	"cursor move"	},
	{	"dl",	NULL,	"delete line"	},
	{	"se",	NULL,	"end standout"	},
	{	"so",	NULL,	"standout"	},
	{	NULL	}
};

struct keycode {
	char	*name,
		*seq;
	int	code;
	char	*desc;
} kc[] = {
	{	"**",	"\033[B",	K_DOWN,	"vt100 down"	},
	{	"**",	"\033[D",	K_LEFT,	"vt100 left"	},
	{	"**",	"\033[C",	K_RGHT,	"vt100 right"	},
	{	"**",	"\033[A",	K_UP,	"vt100 up"	},
	{	"kd",	"\033[B",	K_DOWN,	"key down"	},
	{	"kl",	"\033[D",	K_LEFT,	"key left"	},
	{	"kr",	"\033[C",	K_RGHT,	"key right"	},
	{	"ku",	"\033[A",	K_UP,	"key up"	},
	{	"k1",	"\033Oq",	K_F1,	"key F1"	},
	{	"k2",	"\033Or",	K_F2,	"key F2"	},
	{	"k3",	"\033Os",	K_F3,	"key F3"	},
	{	"k4",	"\033Ot",	K_F4,	"key F4"	},
	{	"k5",	"\033Ou",	K_F5,	"key F5"	},
	{	"k6",	"\033Ov",	K_F6,	"key F6"	},
	{	"k7",	"\033Ow",	K_F7,	"key F7"	},
	{	"k8",	"\033Ox",	K_F8,	"key F8"	},
	{	"k9",	"\033Oy",	K_F9,	"key F9"	},
	{	"k0",	"\033Op",	K_F10,	"key F10"	},
	{	"kP",	"\033Or",	K_PGUP,	"key prev"	},
	{	"kN",	"\033Oq",	K_PGDN,	"key next"	},
	{	"kh",	"\033[H",	K_HOME,	"key home"	},
	{	"kH",	"\033[F",	K_END,	"key eol"	},
	{	"kI",	"\033On",	K_INS,	"key insert"	},
	{	"kD",	"\033D",	K_DEL,	"key delete"	},
	{	NULL	}
};

static struct sgttyb	raw, old;

#define KBUFLEN	64
static char	key_buf[KBUFLEN], key_ptr, key_len;

int	vio_cols,
	vio_lines,
	vio_attr,
	vio_x,
	vio_y;

int	Ansicolor;


int vio_putc(c)
int	c;
{
	char	ch = c;

	write(0, &ch, 1);
	return(c);
}


void vio_puts(s)
char	*s;
{
	write(0, s, strlen(s));
}


void vio_clear() {
	tputs(tc[CL][1], 1, vio_putc);
}


void vio_clreol() {
	tputs(tc[CE][1], 1, vio_putc);
}


void vio_color(c)
int	c;
{
	int	ctab[] = { '0', '4', '2', '6', '1', '5', '3', '7' };
	int	f, b, h;
	char	buf[40];

	vio_attr = c;
	if (Ansicolor) {
		f = ctab[c & 7];
		b = ctab[(c>>4) & 7];
		h = ((c&8) == 8) + '0';
		sprintf(buf, "\033[%c;3%c;4%cm", (char) h, (char) f, (char) b);
		tputs(buf, 1, vio_putc);
	}
	else {
		if (c > 7)
			tputs(tc[SO][1], 1, vio_putc);
		else
			tputs(tc[SE][1], 1, vio_putc);
	}
}


int vio_query() {
	struct fd_set	fdset;
	struct timeval	z;

	FD_ZERO(&fdset);
	FD_SET(0, &fdset);
	z.tv_sec = z.tv_usec = 0L;
	return(select(1, &fdset, NULL, NULL, &z) == 1);
}



int vio_getc() {
	char		ch;
	int		i;
	struct fd_set	fdset;
	struct timeval	z;

rescan:
	if (key_len > key_ptr) {
		return(key_buf[key_ptr++]);
	}

	read(1, &ch, 1);
	if (ch != K_ESC) return(ch);

	FD_ZERO(&fdset);
	FD_SET(0, &fdset);
	z.tv_sec = 0L;
	z.tv_usec = 300 * 1000L;
	if (select(1, &fdset, NULL, NULL, &z) != 1) return(ch);

	key_buf[0] = K_ESC; key_ptr = 1;
	while (1) {
		if (select(1, &fdset, NULL, NULL, &z) != 1) break;
		if (key_ptr >= KBUFLEN-1) break;
		read(1, &ch, 1);
		key_buf[key_ptr++] = ch;
		key_buf[key_ptr] = 0;
		for (i=0; kc[i].name; i++) {
			if (kc[i].seq && !strcmp(key_buf, kc[i].seq)) {
				key_len = key_ptr = 0;
				return(kc[i].code);
			}
		}
	}

	key_len = key_ptr; key_ptr = 0;
	goto rescan;
}


void vio_move(x, y)
int	x, y;
{
	vio_x = x;
	vio_y = y;
	tputs(tgoto(tc[CM][1], vio_x, vio_y), 1, vio_putc);
}


void vio_index(top, bot)
int	top, bot;
{
	vio_move(0, top);
	tputs(tc[DL][1], 25, vio_putc);
}


void vio_revind(top, bot)
int	top, bot;
{
	vio_move(0, top);
	tputs(tc[AL][1], 25, vio_putc);
}


void vio_init() {
	char		*tname, *tp, tbuf[1024], *t;
	static char	tsbuf[1024];
	int		i;

	if ((tname = getenv("TERM")) == NULL) {
		fprintf(stderr, "unixvio: the TERM variable is not set\n");
		exit(EXIT_FAILURE);
	}

	t = getenv("ANSICOLOR");
	if (t != NULL) {
		Ansicolor = !strcmp(t, "no")? 0:
			!strcmp(t, "No")? 0:
			!strcmp(t, "NO")? 0: 1;
	}
	else {
		Ansicolor = strstr(tname, "color") != NULL;
	}

	switch(tgetent(tbuf, tname)) {
		case -1: fprintf(stderr,
			 "unixvio: could not open termcap database");
			 exit(EXIT_FAILURE);
		case 0:  fprintf(stderr,
			"unixvio: unknown terminal type: %s\n", tname);
			 exit(EXIT_FAILURE);
	}

	if (	(vio_cols = tgetnum("co")) == -1 ||
		(vio_lines = tgetnum("li")) == -1
	) {
		fprintf(stderr, "unixvio: cannot determine screen size\n");
		exit(EXIT_FAILURE);
	}

	if (vio_lines > 25) vio_lines = 25;	/*XXX*/

	tp = tsbuf;
	for (i=0; tc[i][0]; i++) {
		if ((tc[i][1] = tgetstr(tc[i][0], &tp)) == NULL) {
			fprintf(stderr, "unixvio: your terminal is lacking the"
			" `%s' capability\n", tc[i][2]);
			exit(EXIT_FAILURE);
		}
	}

	for (i=0; kc[i].name; i++) {
		if ((t = tgetstr(kc[i].name, &tp)) != NULL)
			kc[i].seq = t;
	}

	vio_x = 0;
	vio_y = 0;
	vio_attr = 0x07;

	key_len = key_ptr = 0;

	gtty(0, &old);
	gtty(0, &raw);
	raw.sg_flags |= RAW;
	raw.sg_flags &= ~(ECHO|CRMOD);
	stty(0, &raw);
}


void vio_sync() {
	/* DUMMY */
}


void vio_end() {
	vio_color(7);
	stty(0, &old);
}


void vio_reinit() {
	stty(0, &raw);
}

#endif	/* VIO_EXT */
