/*
 * Copyright (c) 1983-2003, Regents of the University of California.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are 
 * met:
 * 
 * + Redistributions of source code must retain the above copyright 
 *   notice, this list of conditions and the following disclaimer.
 * + 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.
 * + Neither the name of the University of California, San Francisco nor 
 *   the names of its contributors may be used to endorse or promote 
 *   products derived from this software without specific prior written 
 *   permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT 
 * OWNER OR CONTRIBUTORS 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.
 *
 * $OpenBSD: playit.c,v 1.8 2003/06/11 08:45:25 pjanzen Exp $
 * $NetBSD: playit.c,v 1.4 1997/10/20 00:37:15 lukem Exp $
 * $DragonFly: src/games/hunt/hunt/playit.c,v 1.2 2008/09/04 16:12:51 swildner Exp $
 */

#include <sys/types.h>
#include <sys/file.h>
#include <arpa/inet.h>

#include <err.h>
#include <errno.h>
#include <ctype.h>
#include <termios.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include "hunt.h"
#include "display.h"
#include "client.h"

static int	nchar_send;
static FLAG	Last_player;
static int	Otto_expect;

# define	MAX_SEND	5

/*
 * 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++)

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

/*
 * playit:
 *	Play a given game, handling all the curses commands from
 *	the driver.
 */
void
playit(void)
{
	int		ch;
	int		y, x;
	u_int32_t	version;
	int		otto_y, otto_x;
	char		otto_face = ' ';
	int		chars_processed;

	if (read(Socket, &version, sizeof version) != sizeof version) {
		bad_con();
		/* NOTREACHED */
	}
	if (ntohl(version) != (unsigned int)HUNT_VERSION) {
		bad_ver();
		/* NOTREACHED */
	}
	errno = 0;
	nchar_send = MAX_SEND;
	Otto_expect = 0;
	while ((ch = GETCHR()) != EOF) {
		switch (ch & 0377) {
		  case MOVE:
			y = GETCHR();
			x = GETCHR();
			display_move(y, x);
			break;

		  case CLRTOEOL:
			display_clear_eol();
			break;
		  case CLEAR:
			display_clear_the_screen();
			break;
		  case REFRESH:
			display_refresh();
			break;
		  case REDRAW:
			display_redraw_screen();
			display_refresh();
			break;
		  case ENDWIN:
			display_refresh();
			if ((ch = GETCHR()) == LAST_PLAYER)
				Last_player = TRUE;
			ch = EOF;
			goto out;
		  case BELL:
			display_beep();
			break;
		  case READY:
			chars_processed = GETCHR();
			display_refresh();
			if (nchar_send < 0)
				tcflush(STDIN_FILENO, TCIFLUSH);
			nchar_send = MAX_SEND;
			if (Otto_mode) {
				/*
				 * The driver returns the number of keypresses
				 * that it has processed. Use this to figure
				 * out if otto's commands have completed.
				 */
				Otto_expect -= chars_processed;
				if (Otto_expect == 0) {
					/* not very fair! */
					static char buf[MAX_SEND * 2];
					int len;

					/* Ask otto what it wants to do: */
					len = otto(otto_y, otto_x, otto_face,
						buf, sizeof buf);
					if (len) {
						/* Pass it on to the driver: */
						write(Socket, buf, len);
						/* Update expectations: */
						Otto_expect += len;
					}
				}
			}
			break;
		  case ADDCH:
			ch = GETCHR();
			/* FALLTHROUGH */
		  default:
			if (!isprint(ch))
				ch = ' ';
			display_put_ch(ch);
			if (Otto_mode)
				switch (ch) {
				case '<':
				case '>':
				case '^':
				case 'v':
					otto_face = ch;
					display_getyx(&otto_y, &otto_x);
					otto_x--;
					break;
				}
			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(void)
{
	fd_set	readfds, s_readfds;
	int	nfds, s_nfds;

	FD_ZERO(&s_readfds);
	FD_SET(Socket, &s_readfds);
	FD_SET(STDIN_FILENO, &s_readfds);
	s_nfds = (Socket > STDIN_FILENO) ? Socket : STDIN_FILENO;
	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_FILENO, &readfds))
		send_stuff();
	if (!FD_ISSET(Socket, &readfds))
		goto one_more_time;
	icnt = read(Socket, ibuf, sizeof ibuf);
	if (icnt <= 0) {
		bad_con();
		/* NOTREACHED */
	}
	iptr = ibuf;
	icnt--;
	return *iptr++;
}

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

	/* Drain the user's keystrokes: */
	count = read(STDIN_FILENO, Buf, sizeof Buf);
	if (count < 0)
		err(1, "read");
	if (count == 0)
		return;

	if (nchar_send <= 0 && !no_beep) {
		display_beep();
		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';
	for (sp = Buf, nsp = inp; *sp != '\0'; sp++, nsp++) {
		*nsp = map_key[(int)*sp];
		if (*nsp == 'q')
			intr(0);
	}
	count = nsp - inp;
	if (count) {
		nchar_send -= count;
		if (nchar_send < 0)
			count += nchar_send;
		(void) write(Socket, inp, count);
		if (Otto_mode) {
			/*
			 * The user can insert commands over otto.
			 * So, otto shouldn't be alarmed when the 
			 * server processes more than otto asks for.
			 */
			Otto_expect += count;
		}
	}
}

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

	if (Last_player)
		return Q_QUIT;
	if (Otto_mode)
		return otto_quit(old_status);
	display_move(HEIGHT, 0);
	display_put_str("Re-enter game [ynwo]? ");
	display_clear_eol();
	explain = FALSE;
	for (;;) {
		display_refresh();
		if (isupper(ch = getchar()))
			ch = tolower(ch);
		if (ch == 'y')
			return old_status;
		else if (ch == 'o')
			break;
		else if (ch == 'n') {
			display_move(HEIGHT, 0);
			display_put_str("Write a parting message [yn]? ");
			display_clear_eol();
			display_refresh();
			for (;;) {
				if (isupper(ch = getchar()))
					ch = tolower(ch);
				if (ch == 'y')
					goto get_message;
				if (ch == 'n')
					return Q_QUIT;
			}
		}
		else if (ch == 'w') {
			static	char	buf[WIDTH + WIDTH % 2];
			char		*cp, c;

get_message:
			c = ch;		/* save how we got here */
			display_move(HEIGHT, 0);
			display_put_str("Message: ");
			display_clear_eol();
			display_refresh();
			cp = buf;
			for (;;) {
				display_refresh();
				if ((ch = getchar()) == '\n' || ch == '\r')
					break;
				if (display_iserasechar(ch))
				{
					if (cp > buf) {
						int y, x;

						display_getyx(&y, &x);
						display_move(y, x - 1);
						cp -= 1;
						display_clear_eol();
					}
					continue;
				}
				else if (display_iskillchar(ch))
				{
					int y, x;

					display_getyx(&y, &x);
					display_move(y, x - (cp - buf));
					cp = buf;
					display_clear_eol();
					continue;
				} else if (!isprint(ch)) {
					display_beep();
					continue;
				}
				display_put_ch(ch);
				*cp++ = ch;
				if (cp + 1 >= buf + sizeof buf)
					break;
			}
			*cp = '\0';
			Send_message = buf;
			return (c == 'w') ? old_status : Q_MESSAGE;
		}
		display_beep();
		if (!explain) {
			display_put_str("(Yes, No, Write message, or Options) ");
			explain = TRUE;
		}
	}

	display_move(HEIGHT, 0);
	display_put_str("Scan, Cloak, Flying, or Quit? ");
	display_clear_eol();
	display_refresh();
	explain = FALSE;
	for (;;) {
		if (isupper(ch = getchar()))
			ch = tolower(ch);
		if (ch == 's')
			return Q_SCAN;
		else if (ch == 'c')
			return Q_CLOAK;
		else if (ch == 'f')
			return Q_FLY;
		else if (ch == 'q')
			return Q_QUIT;
		display_beep();
		if (!explain) {
			display_put_str("[SCFQ] ");
			explain = TRUE;
		}
		display_refresh();
	}
}

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

	if (read(Socket, &version, sizeof version) != sizeof version) {
		bad_con();
		/* NOTREACHED */
	}
	if (ntohl(version) != (unsigned int)HUNT_VERSION) {
		bad_ver();
		/* NOTREACHED */
	}
	if (write(Socket, Send_message, strlen(Send_message)) < 0) {
		bad_con();
		/* NOTREACHED */
	}
	(void) close(Socket);
}
