/* source/io.c: terminal I/O code, uses the curses package

Copyright (c) 1989-91 James E. Wilson, Robert A. Koeneke

This software may be copied and distributed for educational, research, and not
   for profit purposes provided that this copyright and statement are included
   in all such copies. */

#include <stdio.h>
#ifndef STDIO_LOADED
#define STDIO_LOADED
#endif

#include "config.h"



/* detach from cli process */
/*
long _stack = 70000;
long _priority = 0;
long _BackGroundIO = 1;
char *_procname = "Moria";
*/

#include "amiga.h"
#include "constant.h"
#include "types.h"
#include "externs.h"
#include <ctype.h>
#include <string.h>



#define use_value
#define use_value2

char *getenv();

void exit();
void sleep();

static int curses_on = FALSE;
//static WINDOW *savescr;		/* Spare window for saving the screen. -CJS- */


#ifdef SIGTSTP
/* suspend()							   -CJS- Handle the stop and start
   signals. This ensures that the log is up to date, and that the terminal is
   fully reset and restored.  */
int
suspend()
{
	return 0;
}

#endif

/* initializes curses routines */
void
init_curses()
{
	int i, y, x;

	if (opentimer()== 0) {
		(void) printf("Could not open timer device.\n");
		exit(1);
	}
	if (initscr()== ERR) {
		(void) printf("Error allocating screen in curses package.\n");
		exit(1);
	}
#ifdef SIGTSTP
	(void) signal(SIGTSTP, suspend);
#endif

	(void) clear();

	moriaterm();

	/* check tab settings, exit with error if they are not 8 spaces apart */
}

/* Set up the terminal into a suitable state for moria.	 -CJS- */
void
moriaterm()
{
	curses_on = TRUE;
}


/* Dump IO to buffer					-RAK-	 */
void
put_buffer(out_str, row, col)
	char *out_str;
	int row, col;
{
	vtype tmp_str;

	/* truncate the string, to make sure that it won't go past right edge of
	   screen */
	if (col > 79)
		col = 79;
	(void) strncpy(tmp_str, out_str, 79 - col);

	tmp_str[79 - col] = '\0';

	mvaddstr(row, col, tmp_str);
	//Dump it.
}


/* Dump IO to buffer					-RAK-	 */
void
put_bufferc(out_str, row, col,color)
	char *out_str;
	int row, col,color;
{
	vtype tmp_str;

	/* truncate the string, to make sure that it won't go past right edge of
	   screen */
	if (col > 79)
		col = 79;
	(void) strncpy(tmp_str, out_str, 79 - col);

	tmp_str[79 - col] = '\0';

	mvaddstrc(row, col, tmp_str,color);
	//Dump it.
}


/* Dump the IO buffer to terminal			-RAK-	 */
void
put_qio()
{
	screen_change = TRUE;		/* Let inven_command know something has
								   changed. */

	(void) refresh();
}

/* Put the terminal in the original mode.			   -CJS- */
void
restore_term()
{
	closetimer();
	//amiga

		if (!curses_on)
		return;
	put_qio();					/* Dump any remaining buffer */

	/* this moves curses to bottom right corner */
	//mvcur(stdscr->_cury, stdscr->_curx, LINES - 1, 0);

	exit_curses();				/* exit curses */
	(void) fflush(stdout);

	/* restore the saved values of the special chars */
	curses_on = FALSE;
}


void
shell_out()
{
	put_buffer("This command is not implemented.\n", 0, 0);
}

/* Returns a single character input from the terminal.	This silently -CJS-
   consumes ^R to redraw the screen and reset the terminal, so that this
   operation can always be performed at any input prompt.  inkey() never
   returns ^R.	 */
char
inkey()
{
	int i;

	put_qio();					/* Dump IO buffer		 */
	command_count = 0;			/* Just to be safe -CJS- */
	while (TRUE) {
		i = getch(FALSE);

		/* some machines may not sign extend. */
		if (i == EOF) {
			eof_flag++;
			/* avoid infinite loops while trying to call inkey() for a -more-
			   prompt. */
			msg_flag = FALSE;

			(void) refresh();
			if (!character_generated || character_saved)
				exit_game();
			disturb(1, 0);
			if (eof_flag > 100) {
				/* just in case, to make sure that the process eventually dies */
				panic_save = 1;
				(void) strcpy(died_from, "(end of input: panic saved)");
				if (!save_char()){
					(void) strcpy(died_from, "panic: unexpected eof");
					death = TRUE;
				}
				exit_game();
			}
			return ESCAPE;
		}
//		if (i != CTRL('R'))
			return (char) i;
//		//(void) wrefresh(curscr);
//		moriaterm();
	}
}



/* Flush the buffer					-RAK-	 */
/* Rewritten -HH- */
void
flush()
{
	getch(TRUE); // flush keyboard buffer.
	// flush all pending system messages with a small delay.
	// This will effectively chew up on keyboard repeat.
	// Delay in microseconds.
	flushallmessages(100000); 
}


/* Clears given line of text				-RAK-	 */
void
erase_line(row, col)
	int row;
	int col;
{
	if (row == MSG_LINE && msg_flag)
		msg_print(CNIL);
	clrtoeol(row, col);
}


/* Clears screen */
void
clear_screen()
{
	if (msg_flag)
		msg_print(CNIL);

	(void) clear();
}

void
clear_from(row)
	int row;
{
	clrtobot(row);
}


/* Outputs a char to a given interpolated y, x position	-RAK-	 */
/* sign bit of a character used to indicate standout mode. -CJS */
void
print(ch, row, col)
	char ch;
	int row;
	int col;
{
	vtype tmp_str;

	row -= panel_row_prt;		/* Real co-ords convert to screen positions */
	col -= panel_col_prt;
	mvaddch(row, col, ch);
}


/* Outputs a char to a given interpolated y, x position	-RAK-	 */
/* sign bit of a character used to indicate standout mode. -CJS */
/* Prints the character as graphics on amiga -HH- */
void
printg(ch, row, col)
	char ch;
	int row;
	int col;
{
	vtype tmp_str;

	row -= panel_row_prt;		/* Real co-ords convert to screen positions */
	col -= panel_col_prt;
	mvaddchg(row, col, ch);
}


/* Moves the cursor to a given interpolated y, x position	-RAK-	 */
void
move_cursor_relative(row, col)
	int row;
	int col;
{
	vtype tmp_str;

	row -= panel_row_prt;		/* Real co-ords convert to screen positions */
	col -= panel_col_prt;
	move(row, col);
}



/* Moves the cursor to a given interpolated y, x position	-RAK-	 */
void
move_cursor_relative_high(row, col)
	int row;
	int col;
{
	vtype tmp_str;

	row -= panel_row_prt;		/* Real co-ords convert to screen positions */
	col -= panel_col_prt;
	move_high(row, col);
}


/* Moves the cursor to a given interpolated y, x position	-RAK-	 */
void
move_cursor_relative_low(row, col)
	int row;
	int col;
{
	vtype tmp_str;

	row -= panel_row_prt;		/* Real co-ords convert to screen positions */
	col -= panel_col_prt;
	move_low(row, col);
}


/* Print a message so as not to interrupt a counted command. -CJS- */
void
count_msg_print(p)
	char *p;
{
	int i;

	i = command_count;
	msg_print(p);
	command_count = i;
}


/* Outputs a line to a given y, x position		-RAK-	 */
void
prt(str_buff, row, col)
	char *str_buff;
	int row;
	int col;
{
	if (row == MSG_LINE && msg_flag)
		msg_print(CNIL);
	clrtoeol(row, col);
	put_buffer(str_buff, row, col);
}

/* Outputs a line to a given y, x position	in color	-RAK-	 */
void
prtc(str_buff, row, col,color)
	char *str_buff;
	int row;
	int col,color;
{
	if (row == MSG_LINE && msg_flag)
		msg_print(CNIL);
	clrtoeol(row, col);
	put_bufferc(str_buff, row, col, color);
}


/* move cursor to a given y, x position */
void
move_cursor(row, col)
	int row, col;
{
	(void) move(row, col);
}


/* move cursor to a given y, x position */
void
move_cursor_high(row, col)
	int row, col;
{
	(void) move_high(row, col);
}

/* move cursor to a given y, x position */
void
move_cursor_low(row, col)
	int row, col;
{
	(void) move_low(row, col);
}


/* Outputs message to top line of screen				 */
/* These messages are kept for later reference.	 */
void
msg_print(str_buff)
	char *str_buff;
{
	register int old_len, new_len;
	int combine_messages = FALSE;
	char in_char;

	if (msg_flag) {
		old_len = strlen(old_msg[last_msg]) + 1;

		/* If the new message and the old message are short enough, we want
		   display them together on the same line.  So we don't flush the old
		   message in this case.  */

		if (str_buff)
			new_len = strlen(str_buff);
		else
			new_len = 0;

		if (!str_buff || (new_len + old_len + 2 >= 73)) {
			/* ensure that the complete -more- message is visible. */
			if (old_len > 73)
				old_len = 73;
			put_buffer(" -more-", MSG_LINE, old_len);

			if (!direct_more) {
				/* let sigint handler know that we are waiting for a space */
				wait_for_more = 1;
				do {
					in_char = inkey();
				}
				while ((in_char != ' ') && (in_char != ESCAPE) && (in_char != '\n')
					   && (in_char != '\r'));
				wait_for_more = 0;
			} else {
				if(direct_more_time)
					Delay(direct_more_time * 5);
			}
		} else
			combine_messages = TRUE;
	}
	if (!combine_messages) {
		clrtoeol(MSG_LINE, 0);
	}
	/* Make the null string a special case.  -CJS- */
	if (str_buff) {
		command_count = 0;
		msg_flag = TRUE;

		/* If the new message and the old message are short enough, display
		   them on the same line.  */

		if (combine_messages) {
			put_buffer(str_buff, MSG_LINE, old_len + 2);
			strcat(old_msg[last_msg], "  ");
			strcat(old_msg[last_msg], str_buff);
		} else {
			put_buffer(str_buff, MSG_LINE, 0);
			last_msg++;
			if (last_msg >= MAX_SAVE_MSG)
				last_msg = 0;
			(void) strncpy(old_msg[last_msg], str_buff, VTYPESIZ);
			old_msg[last_msg][VTYPESIZ - 1] = '\0';
		}
	} else
		msg_flag = FALSE;
}


/* Outputs message to top line of screen				 */
/* These messages are kept for later reference.	 */
void
msg_printc(str_buff,color)
	char *str_buff;
	int color;
{
	register int old_len, new_len;
	int combine_messages = FALSE;
	char in_char;

	if (msg_flag) {
		old_len = strlen(old_msg[last_msg]) + 1;

		/* If the new message and the old message are short enough, we want
		   display them together on the same line.  So we don't flush the old
		   message in this case.  */

		if (str_buff)
			new_len = strlen(str_buff);
		else
			new_len = 0;

		if (!str_buff || (new_len + old_len + 2 >= 73)) {
			/* ensure that the complete -more- message is visible. */
			if (old_len > 73)
				old_len = 73;
			put_bufferc(" -more-", MSG_LINE, old_len,1);

			if (!direct_more) {
				/* let sigint handler know that we are waiting for a space */
				wait_for_more = 1;
				do {
					in_char = inkey();
				}
				while ((in_char != ' ') && (in_char != ESCAPE) && (in_char != '\n')
					   && (in_char != '\r'));
				wait_for_more = 0;
			} else {
				if(direct_more_time)
					Delay(direct_more_time * 5);
			}
		} else
			combine_messages = TRUE;
	}
	if (!combine_messages) {
		clrtoeol(MSG_LINE, 0);
	}
	/* Make the null string a special case.  -CJS- */
	if (str_buff) {
		command_count = 0;
		msg_flag = TRUE;

		/* If the new message and the old message are short enough, display
		   them on the same line.  */

		if (combine_messages) {
			put_bufferc(str_buff, MSG_LINE, old_len + 2,color);
			strcat(old_msg[last_msg], "  ");
			strcat(old_msg[last_msg], str_buff);
		} else {
			put_bufferc(str_buff, MSG_LINE, 0,color);
			last_msg++;
			if (last_msg >= MAX_SAVE_MSG)
				last_msg = 0;
			(void) strncpy(old_msg[last_msg], str_buff, VTYPESIZ);
			old_msg[last_msg][VTYPESIZ - 1] = '\0';
		}
	} else
		msg_flag = FALSE;
}

// This msg_print NEVER does an automatic update of the -more- line
// useful when tunneling...
/* Outputs message to top line of screen				 */
/* These messages are kept for later reference.	 */
void
msg_printc2(str_buff,color)
	char *str_buff;
	int color;
{
	register int old_len, new_len;
	int combine_messages = FALSE;
	char in_char;

	if (msg_flag) {
		old_len = strlen(old_msg[last_msg]) + 1;

		/* If the new message and the old message are short enough, we want
		   display them together on the same line.  So we don't flush the old
		   message in this case.  */

		if (str_buff)
			new_len = strlen(str_buff);
		else
			new_len = 0;

		if (!str_buff || (new_len + old_len + 2 >= 73)) {
			/* ensure that the complete -more- message is visible. */
			if (old_len > 73)
				old_len = 73;
			put_bufferc(" -more-", MSG_LINE, old_len,1);

			/* let sigint handler know that we are waiting for a space */
			wait_for_more = 1;
			do {
				in_char = inkey();
			}
			while ((in_char != ' ') && (in_char != ESCAPE) && (in_char != '\n')
				   && (in_char != '\r'));
			wait_for_more = 0;
		} else
			combine_messages = TRUE;
	}
	if (!combine_messages) {
		clrtoeol(MSG_LINE, 0);
	}
	/* Make the null string a special case.  -CJS- */
	if (str_buff) {
		command_count = 0;
		msg_flag = TRUE;

		/* If the new message and the old message are short enough, display
		   them on the same line.  */

		if (combine_messages) {
			put_bufferc(str_buff, MSG_LINE, old_len + 2,color);
			strcat(old_msg[last_msg], "  ");
			strcat(old_msg[last_msg], str_buff);
		} else {
			put_bufferc(str_buff, MSG_LINE, 0,color);
			last_msg++;
			if (last_msg >= MAX_SAVE_MSG)
				last_msg = 0;
			(void) strncpy(old_msg[last_msg], str_buff, VTYPESIZ);
			old_msg[last_msg][VTYPESIZ - 1] = '\0';
		}
	} else
		msg_flag = FALSE;
}


/* Used to verify a choice - user gets the chance to abort choice.  -CJS- */
int
get_check(prompt)
	char *prompt;
{
	int res;
	int y, x;

	prt(prompt, 0, 0);
	x = get_x_cur();
	y = get_y_cur();

	if (x < 73)
		mvaddstr(y, x, " [y/n]");

	do {
		res = inkey();
	}
	while (res == ' ');

	erase_line(0, 0);

	if (res == 'Y' || res == 'y')
		return TRUE;
	else
		return FALSE;
}

/* Prompts (optional) and returns ord value of input char	 */
/* Function returns false if <ESCAPE> is input	 */
int
get_com(prompt, command)
	char *prompt;
	char *command;
{
	int res;

	if (prompt)
		prt(prompt, 0, 0);
	*command = inkey();
	if (*command == ESCAPE)
		res = FALSE;
	else
		res = TRUE;
	erase_line(MSG_LINE, 0);
	return (res);
}

int
myisprint(int i) {
	return (i>31 || i<0);
}


/* Gets a string terminated by <RETURN>		 */
/* Function returns false if <ESCAPE> is input	 */
int
get_string(in_str, row, column, slen)
	char *in_str;
	int row, column, slen;
{

	register int start_col, end_col, i;
	char *p;
	int flag, aborted, tmp;
	int oldrow,oldcol;


	aborted = FALSE;
	flag = FALSE;
	tmp = column;
	for (i = slen; i > 0; i--)
		(void) mvaddch(row, tmp++, ' ');
 	(void) move_high(row, column);
	oldcol = column;
	oldrow = row;
	start_col = column;
	end_col = column + slen - 1;
	if (end_col > 79) {
		slen = 80 - column;
		end_col = 79;
	}
	p = in_str;
	do {
		i = inkey();
		switch (i) {
			case ESCAPE:
				aborted = TRUE;
				break;
			case CTRL('J'):
			case CTRL('M'):
				flag = TRUE;
				break;
			case DELETE:
			case CTRL('H'):
				if (column > start_col) {
					column--;
					put_buffer(" ", row, column);
					move_cursor_low(oldrow,oldcol);
					move_cursor_high(row,column);
					oldrow = row;
					oldcol = column;
					*--p = '\0';
				}
				break;
			default:
				if (!myisprint(i) || column > end_col)
					bell();
				else {
					use_value2 mvaddch(row, column, (char) i);
//					move_cursor_low(oldrow,oldcol);
					move_cursor_high(row,1+column);
					oldrow = row;
					oldcol = column+1;
					*p++ = i;
					column++;
				}
				break;
		}
	}
	while ((!flag) && (!aborted));
	if (aborted) {
		move_cursor_low(oldrow,oldcol);
		return (FALSE);
	}
	/* Remove trailing blanks	 */
	while (p > in_str && p[-1] == ' ')
		p--;
	*p = '\0';
	move_cursor_low(oldrow,oldcol);
	return (TRUE);
}


/* Pauses for user response before returning		-RAK-	 */
void
pause_line(prt_line)
	int prt_line;
{
	prt("[Press any key to continue.]", prt_line, 23);
	(void) inkey();
	erase_line(prt_line, 0);
}


/* Pauses for user response before returning		-RAK-	 */
/* NOTE: Delay is for players trying to roll up "perfect"	 */
/* characters.  Make them wait a bit.			 */
void
pause_exit(prt_line, delay)
	int prt_line;
	int delay;
{
	char dummy;

	prt("[Press any key to continue, or Q to exit.]", prt_line, 10);
	dummy = inkey();
	if (dummy == 'Q') {
		erase_line(prt_line, 0);
		exit_game();
	}
	erase_line(prt_line, 0);
}

void
save_screen()
{
	save_stdscr();
}

void
restore_screen()
{
	restore_stdscr();
}

void
bell()
{
	amiga_bell();
}


/* definitions used by screen_map() */
/* index into border character array */
#define TL 0					/* top left */
#define TR 1
#define BL 2
#define BR 3
#define HE 4					/* horizontal edge */
#define VE 5

/* character set to use */
#define CH(x)	(screen_border[0][x])

/* Display highest priority object in the RATIO by RATIO area */
#define	RATIO 3

void
screen_map_original()
{
	register int i, j;
	static int8u screen_border[2][6] =
	{
		{'+', '+', '+', '+', '-', '|'},	/* normal chars */
		{201, 187, 200, 188, 205, 186}	/* graphics chars */
	};
	int8u map[MAX_WIDTH / RATIO + 1];
	int8u tmp;
	int priority[256];
	int row, orow, col, myrow, mycol = 0;
	char prntscrnbuf[80];

	for (i = 0; i < 256; i++)
		priority[i] = 0;

	priority['<'] = 5;
	priority['>'] = 5;
	priority['@'] = 10;
	priority['#'] = -5;
	priority[216] = -5;
	priority[217] = -5;
	priority[218] = -5;
	priority[219] = -5;
	priority['.'] = -10;
	priority['\''] = -3;
	priority[' '] = -15;

	save_screen();
	clear_screen();

	use_value2 mvaddch(0, 0, CH(TL));

	for (i = 0; i < MAX_WIDTH / RATIO; i++)
		(void) addch(CH(HE));
	(void) 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) {
				/* can not use mvprintw() on ibmpc, because PC-Curses is
				   horribly written, and mvprintw() causes the fp emulation
				   library to be linked with PC-Moria, makes the program 10K
				   bigger */
				(void) sprintf(prntscrnbuf, "%c%s%c", CH(VE), map, CH(VE));
				use_value2 mvaddstrg(orow + 1, 0, prntscrnbuf);
			}
			for (j = 0; j < MAX_WIDTH / RATIO; j++)
				map[j] = ' ';
			orow = row;
		}
		for (j = 0; j < MAX_WIDTH; j++) {
			col = j / RATIO;
			tmp = loc_symbol(i, j);
			if (priority[map[col]] < priority[tmp])
				map[col] = tmp;
			if (map[col] == '@') {
				mycol = col + 1;/* account for border */
				myrow = row + 1;
			}
		}
	}
	if (orow >= 0) {
		(void) sprintf(prntscrnbuf, "%c%s%c", CH(VE), map, CH(VE));
		use_value2 mvaddstrg(orow + 1, 0, prntscrnbuf);
	}
	use_value2 mvaddch(orow + 2, 0, CH(BL));

	for (i = 0; i < MAX_WIDTH / RATIO; i++)
		(void) addch(CH(HE));
	(void) addch(CH(BR));

	use_value2 mvaddstr(23, 23, "Hit any key to continue");

	if (mycol > 0)
		(void) move(myrow, mycol);

	(void) inkey();
	restore_screen();
}


void
screen_map()
{
	register int i, j;
	static int8u screen_border[2][6] =
	{
		{'+', '+', '+', '+', '-', '|'},	/* normal chars */
		{201, 187, 200, 188, 205, 186}	/* graphics chars */
	};
	int8u map[MAX_WIDTH / RATIO + 1];
	int8u tmp;
	int priority[256];
	int row, orow, col, myrow, mycol = 0;
	char prntscrnbuf[80];

	save_screen();
	clear_screen();

	print_screen_map(MAX_HEIGHT, MAX_WIDTH);

	(void) inkey();
	restore_screen();
	return;
}

