#ifdef MSDOS

#include <curses.h>
#ifdef ANSI
#include "ms_ansi.h"
#endif
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <fcntl.h>
#include "config.h"
#include "constant.h"
#include "types.h"
#include "externs.h"

#ifdef LINT_ARGS
void exit(int);
static  unsigned int ioctl(int ,int ,unsigned int );
#else
void exit();
#endif

#define PATHLEN	80
char	moriatop[PATHLEN];
char	moriasav[PATHLEN];
int	saveprompt = TRUE;
int	ibmbios;
static	int	rawio;
byteint	floorsym = '.';
byteint	wallsym = '#';

/* UNIX compatability routines */
char	*
getlogin()
{
	char	*cp;
	char	*getenv();

	if ((cp = getenv("USER")) == NULL)
		cp = "player";
	return cp;
}

/* some prime numbers since these are used for seeds */
getuid() { return UID; }
getgid() { return 7; }
geteuid() { return 13; }

unsigned
sleep(secs)
int	secs;
{
	time_t	finish_time;

	finish_time = time((long *) NULL) + secs;
	while (time((long *) NULL) < finish_time)
		/* nothing */;
	return 0;
}

void
error(fmt, a1, a2, a3, a4)
char	*fmt;
int	a1, a2, a3, a4;
{
	fprintf(stderr, "MORIA error: ");
	fprintf(stderr, fmt, a1, a2, a3, a4);
	(void) sleep(2);
	exit(1);
}

void
warn(fmt, a1, a2, a3, a4)
char	*fmt;
int	a1, a2, a3, a4;
{
	fprintf(stderr, "MORIA warning: ");
	fprintf(stderr, fmt, a1, a2, a3, a4);
	sleep(2);
}

/* Search the path for a file of name "name".  The directory is
 * filled in with the directory part of the path.
 */
FILE *
fopenp(name, mode, directory)
char *name, *mode, directory[];
{
	char *dp, *pathp, *getenv(), lastch;
	FILE *fp;

	/* Try the default directory first.  If the file can't be opened,
	 * start looking along the path.
	 */
	if (fp = fopen(name, mode)) {
		directory[0] = '\0';
		return fp;
	}
	pathp = getenv("PATH");
	while (pathp && *pathp) {
		dp = directory;
		while (*pathp && *pathp != ';')
			lastch = *dp++ = *pathp++;
		if (lastch != '\\' && lastch != '/' && lastch != ':')
			*dp++ = '\\';
		strcpy(dp, name);
		if (fp = fopen(directory, mode)) {
			*dp = '\0';
			return fp;
		}
		if (*pathp)
			pathp++;
	}
	directory[0] = '\0';
	return NULL;
}

/* Read the configuration.
 */
void
msdos_init()
{
	char	buf[BUFSIZ], *bp, opt[PATHLEN];
	int	arg1, arg2, cnt;
	FILE	*fp;

	buf[0] = '\0';
	bp = MORIA_CNF_NAME;
	fp = fopenp(bp, "r", buf);
	strcpy(moriatop, buf);
	strcat(moriatop, MORIA_TOP_NAME);
	strcpy(moriasav, buf);
	strcat(moriasav, MORIA_SAV_NAME);
	if (fp == NULL) {
		warn("Can't find configuration file `%s'\n", bp);
		return;
	}
	printf("Reading configuration from %s%s\n", buf, bp);
	sleep(1);
	while (fgets(buf, sizeof buf, fp)) {
		if (*buf == '#')
			continue;

		cnt = sscanf(buf, "%s", opt);
		if (cnt == 0 || opt[0] == '\0')
			continue;

		/* Go through possible variables
		 */
		if (strcmpi(opt, "GRAPHICS") == 0) {
			cnt = sscanf(buf, "%*s%d%d\n", &arg1, &arg2);
			if (cnt != 2)
				warn("GRAPHICS did not contain 2 values\n");
			else {
				int i;

				wallsym = (byteint) arg1;
				floorsym = (byteint) arg2;

				/* Adjust lists that depend on '#' and '.' */
#define SIZE(x)	sizeof(x)/sizeof(x[0])
				 for (i = 0; i < SIZE(door_list); i++)
					if (door_list[i].tchar == '#')
						door_list[i].tchar = wallsym;
					else if (door_list[i].tchar == '.')
						door_list[i].tchar = floorsym;
				 for (i = 0; i < SIZE(trap_lista); i++)
					if (trap_lista[i].tchar == '#')
						trap_lista[i].tchar = wallsym;
					else if (trap_lista[i].tchar == '.')
						trap_lista[i].tchar = floorsym;
			}
		}
		else if (strcmpi(opt, "SAVE") == 0) {
			cnt = sscanf(buf, "%*s%s", opt);
			if (cnt == 0)
				warn("SAVE option requires a filename\n");
			else {
				if (bp = strchr(opt, ';')) {
					*bp++ = '\0';
					if (*bp == 'n' || *bp == 'N')
						saveprompt = FALSE;
				}
				if (opt[0])
					(void) strcpy(moriasav, opt);
			}
		}
		else if (strcmpi(opt, "SCORE") == 0) {
			cnt = sscanf(buf, "%*s%s", opt);
			if (cnt == 0)
				warn("SCORE option requires a filename\n");
			else
				(void) strcpy(moriatop, opt);
		}
		else if (strcmpi(opt, "KEYBOARD") == 0) {
			cnt = sscanf(buf, "%*s%s", opt);
			if (cnt == 0)
				warn("KEYBOARD option requires a value\n");
			else if (strcmpi(opt, "ROGUE") == 0)
				key_bindings = ROGUE_LIKE;
			else if (strcmpi(opt, "VMS") == 0)
				key_bindings = ORIGINAL;
		}
		else if (strcmpi(opt, "IBMBIOS") == 0)
			ibmbios = TRUE;
		else if (strcmpi(opt, "RAWIO") == 0)
			rawio = TRUE;
#ifdef ANSI
		/* Usage: ANSI [ check_ansi [ domoveopt [ tgoto ] ] ]
		 * where check_ansi and domoveopt are "Y"es unless explicitly
		 * set to "N"o.  Tgoto is "N"o unless set to "Y"es.
		 */
		else if (strcmpi(opt, "ANSI") == 0) {
			cnt=sscanf(buf, "%*s%1s%1s%1s",&opt[0],&opt[1],&opt[2]);
			ansi_prep(cnt < 1 || opt[0] == 'y' || opt[0] == 'Y',
				 cnt < 2 || opt[1] == 'y' || opt[1] == 'Y',
				 cnt >= 3 && (opt[2] == 'y' || opt[2] == 'Y'));
		}
#endif
		else
			warn("Unknown configuration line: `%s'\n", buf);
	}
	fclose(fp);

	/* The only text file has been read.  Switch to binary mode */
}

#include <dos.h>
#define DEVICE	0x80
#define RAW	0x20
#define IOCTL	0x44
#define STDIN	fileno(stdin)
#define STDOUT	fileno(stdout)
#define GETBITS	0
#define SETBITS	1

static unsigned	old_stdin, old_stdout, ioctl();

void
msdos_raw() {
	if (!rawio)
		return;
	old_stdin = ioctl(STDIN, GETBITS, 0);
	old_stdout = ioctl(STDOUT, GETBITS, 0);
	if (old_stdin & DEVICE)
		ioctl(STDIN, SETBITS, old_stdin | RAW);
	if (old_stdout & DEVICE)
		ioctl(STDOUT, SETBITS, old_stdout | RAW);
}

void
msdos_noraw() {
	if (!rawio)
		return;
	if (old_stdin)
		(void) ioctl(STDIN, SETBITS, old_stdin);
	if (old_stdout)
		(void) ioctl(STDOUT, SETBITS, old_stdout);
}

static unsigned
ioctl(handle, mode, setvalue)
unsigned setvalue;
{
	union REGS regs;

	regs.h.ah = IOCTL;
	regs.h.al = (unsigned char) mode;
	regs.x.bx = handle;
	regs.h.dl = (unsigned char) setvalue;
	regs.h.dh = 0;			/* Zero out dh */
	intdos(&regs, &regs);
	return (regs.x.dx);
}

/* Normal characters are output when the shift key is not pushed.
 * Shift characters are output when either shift key is pushed.
 */
#define KEYPADHI	83
#define KEYPADLOW	71
#define ISKEYPAD(x)	(KEYPADLOW <= (x) && (x) <= KEYPADHI)
#define CTRL(x)		(x - '@')
typedef struct {
	char normal, shift, numlock;
} KEY;
static KEY roguekeypad[KEYPADHI - KEYPADLOW + 1] = {
	{'y', 'Y', CTRL('Y')},			/* 7 */
	{'k', 'K', CTRL('K')},			/* 8 */
	{'u', 'U', CTRL('U')},			/* 9 */
	{'.', '.', '.'},			/* - */
	{'h', 'H', CTRL('H')},			/* 4 */
	{'.', '.', '.'},			/* 5 */
	{'l', 'L', CTRL('L')},			/* 6 */
	{CTRL('P'), CTRL('P'), CTRL('P')},	/* + */
	{'b', 'B', CTRL('B')},			/* 1 */
	{'j', 'J', CTRL('J')},			/* 2 */
	{'n', 'N', CTRL('N')},			/* 3 */
	{'i', 'i', 'i'},			/* Ins */
	{'.', '.', '.'}				/* Del */
};
static KEY originalkeypad[KEYPADHI - KEYPADLOW + 1] = {
	{55, 55, 55},			/* 7 */
	{56, 56, 56},			/* 8 */
	{57, 57, 57},			/* 9 */
	{53, 53, 53},			/* - */
	{52, 52, 52},			/* 4 */
	{53, 53, 53},			/* 5  - move */
	{54, 54, 54},			/* 6 */
	{CTRL('M'), CTRL('M'), CTRL('M')},	/* + */
	{49, 49, 49},			/* 1 */
	{50, 50, 50},			/* 2 */
	{51, 51, 51},			/* 3 */
	{'i', 'i', 'i'},		/* Ins */
	{'.', '.', '.'}			/* Del */
};

/* bios_getch gets keys directly with a BIOS call.
 */
#define SHIFT		(0x1 | 0x2)
#define NUMLOCK		0x20
#define KEYBRD_BIOS	0x16

int
bios_getch()
{
	unsigned char scan, shift;
	int	ch;
	KEY	*kp;
	union REGS regs;

	if (key_bindings == ROGUE_LIKE)
		kp = roguekeypad;
	else
		kp = originalkeypad;

	/* Get scan code.
	 */
	regs.h.ah = 0;
	int86(KEYBRD_BIOS, &regs, &regs);
	ch = regs.h.al;
	scan = regs.h.ah;

	/* Get shift status.
	 */
	regs.h.ah = 2;
	int86(KEYBRD_BIOS, &regs, &regs);
	shift = regs.h.al;

	/* If scan code is for the keypad, translate it.
	 */
	if (ISKEYPAD(scan)) {
		if (shift & NUMLOCK)
			ch = kp[scan - KEYPADLOW].numlock;
		else if (shift & SHIFT)
			ch = kp[scan - KEYPADLOW].shift;
		else
			ch = kp[scan - KEYPADLOW].normal;
	}
	return ch;
}

int
msdos_getch()
{
	int	ch;

	if (ibmbios)
		ch = bios_getch();
	else {
		ch = getch();
		if (ch == 0)
			ch = getch();
	}
	return ch;
}

/* Hardcode the introductory message in */
void
msdos_intro()
{
	clear_screen(0, 0);
	mvprintw(0,0,"                         *********************");
	mvprintw(1,0,"                         **   Moria %5.3f   **",
	  CUR_VERSION);
	mvprintw(2,0,"                         *********************");
	mvprintw(3,0,"                   COPYRIGHT (c) Robert Alan Koeneke");
	mvprintw(5,0,"Programmers : Robert Alan Koeneke / University of Oklahoma");
	mvprintw(6,0,"              Jimmey Wayne Todd   / University of Oklahoma");
	mvprintw(8,0,"UNIX Port   : James E. Wilson     / UC Berkeley");
	mvprintw(10,0,"MSDOS Port  : Don Kneller         / 1349 - 10th ave");
	mvprintw(11,0,"                                  / San Francisco, CA 94122");
	mvprintw(12,0,"                                  / Dec 12, 1988");
	pause_line(23);
}

#ifdef SCREENMAP
void
msdos_print_map()
{
#define TL	0	/* top left */
#define TR	1
#define BL	2
#define BR	3
#define HE	4	/* horizontal edge */
#define VE	5
	register int	i, j;
	static byteint border[2][6] = {
		{'+', '+', '+', '+', '-', '|'},	/* normal chars */
		{201, 187, 200, 188, 205, 186}	/* graphics chars */
		};
#ifdef MSDOS
# ifdef ANSI
#   define CH(x)	(ansi ? border[0][x] : border[1][x])
# else
#   define CH(x)	(border[1][x])
# endif
#else
#   define CH(x)	(border[0][x])
#endif
	
/* Display highest priority object in the RATIO by RATIO area */
#define	RATIO	3
	byteint	map[MAX_WIDTH / RATIO + 1];
	byteint	tmp;
	int	priority[256];
	int	row, orow, col, myrow, mycol = 0;
	char	dummy;

	for (i = 0; i < 256; i++)
		priority[i] = 0;
	priority['<'] = 10;
	priority['>'] = 10;
	priority['@'] = 5;
	priority[wallsym] = -5;
	priority['\''] = -5;
	priority[floorsym] = -0;
	priority[' '] = -15;

	clear_screen(0, 0);
	mvaddch(1, 0, CH(TL));
	for (i = 0; i < MAX_WIDTH / RATIO; i++)
		addch(CH(HE));
	addch(CH(TR));
	orow = -1;
	map[MAX_WIDTH / RATIO] = '\0';
	for (i = 0; i < MAX_HEIGHT; i++) {
		row = i / RATIO;
		if (row != orow) {
			if (orow >= 0)
				mvprintw(orow+2,0,"%c%s%c",CH(VE), map, CH(VE));
			for (j = 0; j < MAX_WIDTH / RATIO; j++)
				map[j] = ' ';
			orow = row;
		}
		for (j = 0; j < MAX_WIDTH; j++) {
			col = j / RATIO;
			if (test_light(i, j))
				loc_symbol(i, j, &tmp);
			else
				tmp = ' ';
			if (priority[map[col]] < priority[tmp])
				map[col] = tmp;
			if (map[col] == '@') {
				mycol = col + 1; /* account for border */
				myrow = row + 2; /* border and line */
			}
		}
	}
	if (orow >= 0)
		mvprintw(orow + 2, 0, "%c%s%c", CH(VE), map, CH(VE));
	mvaddch(orow + 3, 0, CH(BL));
	for (i = 0; i < MAX_WIDTH / RATIO; i++)
		addch(CH(HE));
	addch(CH(BR));
	prt("Hit any key to continue", 0, 0);
	if (mycol > 0)
		move(myrow, mycol);
	inkey(&dummy);
	draw_cave();
}
#endif

#ifdef PC_CURSES
/* Seems to be a bug in PCcurses whereby it won't really clear the screen 
 * if there are characters there it doesn't know about.
 */
#define VIDEOINT	0x10
void
bios_clear()
{
	union REGS regs;
	unsigned char	nocols, activepage;
	extern int	LI;

#ifdef ANSI
	if (ansi)
		return;
#endif

	/* get video attributes */
	regs.h.ah = 15;
	int86(VIDEOINT, &regs, &regs);
	nocols = regs.h.ah;
	activepage = regs.h.bh;

	/* Move to lower right corner */
	regs.h.ah = 2;
	regs.h.dh = (unsigned char) LI;
	regs.h.dl = nocols - 1;	/* lower right col */
	regs.h.bh = activepage;
	int86(VIDEOINT, &regs, &regs);

	/* get current attribute into bh */
	regs.h.ah = 8;
	regs.h.bh = activepage;
	int86(VIDEOINT, &regs, &regs);
	regs.h.bh = regs.h.ah;

	regs.h.cl = 0;	/* upper left row */
	regs.h.ch = 0;	/* upper left col */
	regs.h.dh = (unsigned char) LI;	/* lower right row */
	regs.h.dl = nocols - 1;	/* lower right col */
	regs.h.al = 0;	/* clear window */
	regs.h.ah = 7;	/* scroll down */
	int86(VIDEOINT, &regs, &regs);
}
#endif

#endif
