/*	$NetBSD: playit.c,v 1.4 1997/10/20 00:37:15 lukem Exp $	*/
/*
 *  Hunt
 *  Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
 *  San Francisco, California
 */

#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: playit.c,v 1.4 1997/10/20 00:37:15 lukem Exp $");
#endif /* not lint */

# include	<sys/file.h>
# include	<err.h>
# include	<errno.h>
# include	<curses.h>
# include	<ctype.h>
# include	<signal.h>
# include	<sys/time.h>
# if defined(HPUX) || (defined(BSD_RELEASE) && BSD_RELEASE >= 44)
# include	<termios.h>
# include	<unistd.h>
# endif
# include	"hunt.h"

# ifndef FREAD
# define	FREAD	1
# endif

# if !defined(USE_CURSES) || !defined(TERMINFO)
# define	beep()		(void) putchar(CTRL('G'))
# endif
# if !defined(USE_CURSES)
# undef		refresh
# define	refresh()	(void) fflush(stdout);
# endif
# ifdef USE_CURSES
# define	clear_eol()	clrtoeol()
# define	put_ch		addch
# define	put_str		addstr
# endif

static int	nchar_send;
# ifndef USE_CURSES
char		screen[SCREEN_HEIGHT][SCREEN_WIDTH2], blanks[SCREEN_WIDTH];
int		cur_row, cur_col;
# endif
# ifdef OTTO
int		Otto_count;
int		Otto_mode;
static int	otto_y, otto_x;
static char	otto_face;
# endif

# define	MAX_SEND	5
# define	STDIN		0

/*
 * ibuf is the input buffer used for the stream from the driver.
 * It is small because we do not check for user input when there
 * are characters in the input buffer.
 */
static int		icnt = 0;
static unsigned char	ibuf[256], *iptr = ibuf;

#define	GETCHR()	(--icnt < 0 ? getchr() : *iptr++)

#if !defined(BSD_RELEASE) || BSD_RELEASE < 44
extern int	_putchar();
#endif

static	unsigned char	getchr __P((void));
static	void		send_stuff __P((void));

/*
 * playit:
 *	Play a given game, handling all the curses commands from
 *	the driver.
 */
void
playit()
{
	int		ch;
	int		y, x;
	long		version;

	if (read(Socket, (char *) &version, LONGLEN) != LONGLEN) {
		bad_con();
		/* NOTREACHED */
	}
	if (ntohl(version) != (unsigned long)HUNT_VERSION) {
		bad_ver();
		/* NOTREACHED */
	}
	errno = 0;
# ifdef OTTO
	Otto_count = 0;
# endif
	nchar_send = MAX_SEND;
	while ((ch = GETCHR()) != EOF) {
# ifdef DEBUG
		fputc(ch, stderr);
# endif
		switch (ch & 0377) {
		  case MOVE:
			y = GETCHR();
			x = GETCHR();
# ifdef USE_CURSES
			move(y, x);
# else
			mvcur(cur_row, cur_col, y, x);
			cur_row = y;
			cur_col = x;
# endif
			break;
		  case ADDCH:
			ch = GETCHR();
# ifdef OTTO
			switch (ch) {

			case '<':
			case '>':
			case '^':
			case 'v':
				otto_face = ch;
# ifdef USE_CURSES
				getyx(stdscr, otto_y, otto_x);
# else
				otto_y = cur_row;
				otto_x = cur_col;
# endif
				break;
			}
# endif
			put_ch(ch);
			break;
		  case CLRTOEOL:
			clear_eol();
			break;
		  case CLEAR:
			clear_the_screen();
			break;
		  case REFRESH:
			refresh();
			break;
		  case REDRAW:
			redraw_screen();
			refresh();
			break;
		  case ENDWIN:
			refresh();
			if ((ch = GETCHR()) == LAST_PLAYER)
				Last_player = TRUE;
			ch = EOF;
			goto out;
		  case BELL:
			beep();
			break;
		  case READY:
			refresh();
			if (nchar_send < 0)
# if defined(HPUX) || (defined(BSD_RELEASE) && BSD_RELEASE >= 44)
				tcflush(STDIN, TCIFLUSH);
# else
# ifndef TCFLSH
				(void) ioctl(STDIN, TIOCFLUSH, &in);
# else
				(void) ioctl(STDIN, TCFLSH, 0);
# endif
# endif
			nchar_send = MAX_SEND;
# ifndef OTTO
			(void) GETCHR();
# else
			Otto_count -= (GETCHR() & 0xff);
			if (!Am_monitor) {
# ifdef DEBUG
				fputc('0' + Otto_count, stderr);
# endif
				if (Otto_count == 0 && Otto_mode)
					otto(otto_y, otto_x, otto_face);
			}
# endif
			break;
		  default:
# ifdef OTTO
			switch (ch) {

			case '<':
			case '>':
			case '^':
			case 'v':
				otto_face = ch;
# ifdef USE_CURSES
				getyx(stdscr, otto_y, otto_x);
# else
				otto_y = cur_row;
				otto_x = cur_col;
# endif
				break;
			}
# endif
			put_ch(ch);
			break;
		}
	}
out:
	(void) close(Socket);
}

/*
 * getchr:
 *	Grab input and pass it along to the driver
 *	Return any characters from the driver
 *	When this routine is called by GETCHR, we already know there are
 *	no characters in the input buffer.
 */
static unsigned char
getchr()
{
	fd_set	readfds, s_readfds;
	int	nfds, s_nfds;

	FD_ZERO(&s_readfds);
	FD_SET(Socket, &s_readfds);
	FD_SET(STDIN, &s_readfds);
	s_nfds = (Socket > STDIN) ? Socket : STDIN;
	s_nfds++;

one_more_time:
	do {
		errno = 0;
		readfds = s_readfds;
		nfds = s_nfds;
		nfds = select(nfds, &readfds, NULL, NULL, NULL);
	} while (nfds <= 0 && errno == EINTR);

	if (FD_ISSET(STDIN, &readfds))
		send_stuff();
	if (! FD_ISSET(Socket, &readfds))
		goto one_more_time;
	icnt = read(Socket, ibuf, sizeof ibuf);
	if (icnt < 0) {
		bad_con();
		/* NOTREACHED */
	}
	if (icnt == 0)
		goto one_more_time;
	iptr = ibuf;
	icnt--;
	return *iptr++;
}

/*
 * send_stuff:
 *	Send standard input characters to the driver
 */
static void
send_stuff()
{
	int		count;
	char		*sp, *nsp;
	static char	inp[sizeof Buf];

	count = read(STDIN, Buf, sizeof Buf);
	if (count <= 0)
		return;
	if (nchar_send <= 0 && !no_beep) {
		(void) write(1, "\7", 1);	/* CTRL('G') */
		return;
	}

	/*
	 * look for 'q'uit commands; if we find one,
	 * confirm it.  If it is not confirmed, strip
	 * it out of the input
	 */
	Buf[count] = '\0';
	nsp = inp;
	for (sp = Buf; *sp != '\0'; sp++)
		if ((*nsp = map_key[(int)*sp]) == 'q')
			intr(0);
		else
			nsp++;
	count = nsp - inp;
	if (count) {
# ifdef OTTO
		Otto_count += count;
# endif
		nchar_send -= count;
		if (nchar_send < 0)
			count += nchar_send;
		(void) write(Socket, inp, count);
	}
}

/*
 * quit:
 *	Handle the end of the game when the player dies
 */
int
quit(old_status)
	int	old_status;
{
	int	explain, ch;

	if (Last_player)
		return Q_QUIT;
# ifdef OTTO
	if (Otto_mode)
		return Q_CLOAK;
# endif
# ifdef USE_CURSES
	move(HEIGHT, 0);
# else
	mvcur(cur_row, cur_col, HEIGHT, 0);
	cur_row = HEIGHT;
	cur_col = 0;
# endif
	put_str("Re-enter game [ynwo]? ");
	clear_eol();
	explain = FALSE;
	for (;;) {
		refresh();
		if (isupper(ch = getchar()))
			ch = tolower(ch);
		if (ch == 'y')
			return old_status;
		else if (ch == 'o')
			break;
		else if (ch == 'n') {
# ifndef INTERNET
			return Q_QUIT;
# else
# ifdef USE_CURSES
			move(HEIGHT, 0);
# else
			mvcur(cur_row, cur_col, HEIGHT, 0);
			cur_row = HEIGHT;
			cur_col = 0;
# endif
			put_str("Write a parting message [yn]? ");
			clear_eol();
			refresh();
			for (;;) {
				if (isupper(ch = getchar()))
					ch = tolower(ch);
				if (ch == 'y')
					goto get_message;
				if (ch == 'n')
					return Q_QUIT;
			}
# endif
		}
# ifdef INTERNET
		else if (ch == 'w') {
			static	char	buf[WIDTH + WIDTH % 2];
			char		*cp, c;

get_message:
			c = ch;		/* save how we got here */
# ifdef USE_CURSES
			move(HEIGHT, 0);
# else
			mvcur(cur_row, cur_col, HEIGHT, 0);
			cur_row = HEIGHT;
			cur_col = 0;
# endif
			put_str("Message: ");
			clear_eol();
			refresh();
			cp = buf;
			for (;;) {
				refresh();
				if ((ch = getchar()) == '\n' || ch == '\r')
					break;
# if defined(TERMINFO) || BSD_RELEASE >= 44
				if (ch == erasechar())
# else
				if (ch == _tty.sg_erase)
# endif
				{
					if (cp > buf) {
# ifdef USE_CURSES
						int y, x;
						getyx(stdscr, y, x);
						move(y, x - 1);
# else
						mvcur(cur_row, cur_col, cur_row,
								cur_col - 1);
						cur_col -= 1;
# endif
						cp -= 1;
						clear_eol();
					}
					continue;
				}
# if defined(TERMINFO) || BSD_RELEASE >= 44
				else if (ch == killchar())
# else
				else if (ch == _tty.sg_kill)
# endif
				{
# ifdef USE_CURSES
					int y, x;
					getyx(stdscr, y, x);
					move(y, x - (cp - buf));
# else
					mvcur(cur_row, cur_col, cur_row,
							cur_col - (cp - buf));
					cur_col -= cp - buf;
# endif
					cp = buf;
					clear_eol();
					continue;
				} else if (!isprint(ch)) {
					beep();
					continue;
				}
				put_ch(ch);
				*cp++ = ch;
				if (cp + 1 >= buf + sizeof buf)
					break;
			}
			*cp = '\0';
			Send_message = buf;
			return (c == 'w') ? old_status : Q_MESSAGE;
		}
# endif
		beep();
		if (!explain) {
			put_str("(Yes, No, Write message, or Options) ");
			explain = TRUE;
		}
	}

# ifdef USE_CURSES
	move(HEIGHT, 0);
# else
	mvcur(cur_row, cur_col, HEIGHT, 0);
	cur_row = HEIGHT;
	cur_col = 0;
# endif
# ifdef FLY
	put_str("Scan, Cloak, Flying, or Quit? ");
# else
	put_str("Scan, Cloak, or Quit? ");
# endif
	clear_eol();
	refresh();
	explain = FALSE;
	for (;;) {
		if (isupper(ch = getchar()))
			ch = tolower(ch);
		if (ch == 's')
			return Q_SCAN;
		else if (ch == 'c')
			return Q_CLOAK;
# ifdef FLY
		else if (ch == 'f')
			return Q_FLY;
# endif
		else if (ch == 'q')
			return Q_QUIT;
		beep();
		if (!explain) {
# ifdef FLY
			put_str("[SCFQ] ");
# else
			put_str("[SCQ] ");
# endif
			explain = TRUE;
		}
		refresh();
	}
}

# ifndef USE_CURSES
void
put_ch(ch)
	char	ch;
{
	if (!isprint(ch)) {
		fprintf(stderr, "r,c,ch: %d,%d,%d", cur_row, cur_col, ch);
		return;
	}
	screen[cur_row][cur_col] = ch;
	putchar(ch);
	if (++cur_col >= COLS) {
		if (!AM || XN)
			putchar('\n');
		cur_col = 0;
		if (++cur_row >= LINES)
			cur_row = LINES;
	}
}

void
put_str(s)
	const char	*s;
{
	while (*s)
		put_ch(*s++);
}
# endif

void
clear_the_screen()
{
# ifdef USE_CURSES
	clear();
	move(0, 0);
	refresh();
# else
	int	i;

	if (blanks[0] == '\0')
		for (i = 0; i < SCREEN_WIDTH; i++)
			blanks[i] = ' ';

	if (CL != NULL) {
#if !defined(BSD_RELEASE) || BSD_RELEASE < 44
		tputs(CL, LINES, _putchar);
#else
		tputs(CL, LINES, __cputchar);
#endif
		for (i = 0; i < SCREEN_HEIGHT; i++)
			memcpy(screen[i], blanks, SCREEN_WIDTH);
	} else {
		for (i = 0; i < SCREEN_HEIGHT; i++) {
			mvcur(cur_row, cur_col, i, 0);
			cur_row = i;
			cur_col = 0;
			clear_eol();
		}
		mvcur(cur_row, cur_col, 0, 0);
	}
	cur_row = cur_col = 0;
#endif
}

#ifndef USE_CURSES
void
clear_eol()
{
	if (CE != NULL)
#if !defined(BSD_RELEASE) || BSD_RELEASE < 44
		tputs(CE, 1, _putchar);
#else
		tputs(CE, 1, __cputchar);
#endif
	else {
		fwrite(blanks, sizeof (char), SCREEN_WIDTH - cur_col, stdout);
		if (COLS != SCREEN_WIDTH)
			mvcur(cur_row, SCREEN_WIDTH, cur_row, cur_col);
		else if (AM)
			mvcur(cur_row + 1, 0, cur_row, cur_col);
		else
			mvcur(cur_row, SCREEN_WIDTH - 1, cur_row, cur_col);
	}
	memcpy(&screen[cur_row][cur_col], blanks, SCREEN_WIDTH - cur_col);
}
# endif

void
redraw_screen()
{
# ifdef USE_CURSES
	clearok(stdscr, TRUE);
	touchwin(stdscr);
# else
	int		i;
# ifndef NOCURSES
	static int	first = 1;

	if (first) {
		curscr = newwin(SCREEN_HEIGHT, SCREEN_WIDTH, 0, 0);
		if (curscr == NULL)
			errx(1, "Can't create curscr");
# if !defined(BSD_RELEASE) || BSD_RELEASE < 44
		for (i = 0; i < SCREEN_HEIGHT; i++)
			curscr->_y[i] = screen[i];
# endif
		first = 0;
	}
# if defined(BSD_RELEASE) && BSD_RELEASE >= 44
	for (i = 0; i < SCREEN_HEIGHT; i++) {
		int	j;

		for (j = 0; j < SCREEN_WIDTH; j++)
			curscr->lines[i]->line[j].ch = screen[i][j];
	}
	curscr->cury = cur_row;
	curscr->curx = cur_col;
# else
	curscr->_cury = cur_row;
	curscr->_curx = cur_col;
# endif
	clearok(curscr, TRUE);
	touchwin(curscr);
	wrefresh(curscr);
#else
	mvcur(cur_row, cur_col, 0, 0);
	for (i = 0; i < SCREEN_HEIGHT - 1; i++) {
		fwrite(screen[i], sizeof (char), SCREEN_WIDTH, stdout);
		if (COLS > SCREEN_WIDTH || (COLS == SCREEN_WIDTH && !AM))
			putchar('\n');
	}
	fwrite(screen[SCREEN_HEIGHT - 1], sizeof (char), SCREEN_WIDTH - 1,
		stdout);
	mvcur(SCREEN_HEIGHT - 1, SCREEN_WIDTH - 1, cur_row, cur_col);
#endif
#endif
}

/*
 * do_message:
 *	Send a message to the driver and return
 */
void
do_message()
{
	long	version;

	if (read(Socket, (char *) &version, LONGLEN) != LONGLEN) {
		bad_con();
		/* NOTREACHED */
	}
	if (ntohl(version) != (unsigned long)HUNT_VERSION) {
		bad_ver();
		/* NOTREACHED */
	}
# ifdef INTERNET
	if (write(Socket, Send_message, strlen(Send_message)) < 0) {
		bad_con();
		/* NOTREACHED */
	}
# endif
	(void) close(Socket);
}
