/* displays a screenful of text from an array */

#include <sys/time.h>
#include <stdio.h>
#include <curses.h>

#include "pdb.h"

#define	WIN_TOP		3
#define	MAX_TXT_HEAD	16

#define	MSG_LEN		80

int	curses_active = 0;

/*static*/int	text_numline,		/* Num of lines in text buffer */
		text_curline;		/* Current (top of screen) line */

int	window_len;		/* Current length of our pager window */

int	so_line, so_col, so_len;

static char	text_heading[MAX_TXT_HEAD][TXT_LLEN];

static char	Msg[MSG_LEN];
static char	ErrMsg[MSG_LEN];
/*static*/char	Heading[MSG_LEN];

char	*(*display_func)();

static char	help_msg[] =
"Use up, down, top & bottom to scroll.  Type help for a full list of commands.";

clear_text_headings()
{
	int	i;

	for (i = 0; i < MAX_TXT_HEAD; i++)
		*text_heading[i] = '\0';
	window_len = LINES - (WIN_TOP + 3);
}

void
text_window_reset()
{
	text_numline = text_curline = 0;
	clear_text_headings();
	display_func = 0;
}

void
set_text_heading(hn, str)
int	hn;
char	str[];
{
	if (hn >= MAX_TXT_HEAD)
		return;
	if (hn < 0)
		hn = hn + MAX_TXT_HEAD;
	if (!*text_heading[hn])
		window_len--;
	if (*str == '\\') {
		int	i;
		for (str++, i = 0; i < TXT_LLEN; i++)
			text_heading[hn][i] = *str;
	} else
		bcopy(str, text_heading[hn], TXT_LLEN);
}

#ifdef	BSD_UNIX_SPECIFIC

/* VARARGS2 */
fmtbuf(buf, fmt, args)
char	*buf, *fmt;
{
	struct _iobuf	iobuf;

	iobuf._flag = _IOWRT+_IOSTRG;
	iobuf._ptr = buf;
	iobuf._cnt = 32767;
	_doprnt(fmt, args, &iobuf);
	putc('\0', &iobuf);
}

#endif

/*
 * The folling argument stuff isn't very pretty, buts its
 * relatively portable.
 */

#define	VAR_ARG_LIST a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16
#define	VAR_ARG_DECL long

more_msg(str)
char	str[];
{
/*	cbreak(), noecho(); */
	mvaddstr(LINES - 1, 0, str);
	addstr(" --More--"), clrtoeol();
	flush(), refresh();
	getchar();
/*	nocbreak(), echo(); */
}

/* VARARGS1 */
msgnow(fmt, VAR_ARG_LIST)
char		*fmt;
VAR_ARG_DECL	VAR_ARG_LIST;
{
	char	buf[256];

	sprintf(buf, fmt, VAR_ARG_LIST);
	if (curses_active) {
		if (*buf == '@')
			more_msg(buf + 1);
		else {
			mvaddstr(LINES - 1, 0, buf), clrtoeol();
			refresh(), flush();
		}
	} else
		putstr(buf);
}

/* VARARGS1 */
setmsg(fmt, VAR_ARG_LIST)
char		*fmt;
VAR_ARG_DECL	VAR_ARG_LIST;
{
	char	buf[256];

	if (curses_active && *Msg)
		more_msg(Msg);
	sprintf(buf, fmt, VAR_ARG_LIST);
	buf[MSG_LEN] = '\0';
	if (curses_active)
		strcpy(Msg, buf);
	else
		putstr(buf);
}

/* VARARGS1 */
errmsg(fmt, VAR_ARG_LIST)
char		*fmt;
VAR_ARG_DECL	VAR_ARG_LIST;
{
	char	buf[256];

	if (curses_active && *ErrMsg)
		more_msg(ErrMsg);
	sprintf(buf, fmt, VAR_ARG_LIST);
	buf[MSG_LEN] = '\0';
	strcpy(ErrMsg, buf);
	if (curses_active)
		strcpy(ErrMsg, buf);
	else
		putstr(buf);
}

error(str)
char	str[];
{
	extern int	errno, sys_nerr;
	extern char	*sys_errlist[];

	if (errno >= sys_nerr)
		errmsg("%s: falure code %d", str, errno);
	else
		errmsg("%s: %s", str, sys_errlist[errno]);
}


display_set(lines, func)
int	lines;
char	*(*func)();
{
	text_numline = lines;
	display_func = func;

	/*
	 * make it so a negative line# flags that we should call the routine
	 * for lines we know it doest have so it can display its own stuff
	 */

}


static void
display_screen()
{
	int	i, numhead, linecnt, screens, screen_num;
	long	secs;
	char	where_buf[32];
	
	if (text_curline < 0)
		text_curline = 0;
	screens = text_numline / window_len;
	if (text_numline % window_len)
		screens++;
	if (text_curline >= text_numline)
		text_curline = text_numline - 1;
	if (text_curline % window_len)
		text_curline -= text_curline % window_len;

	screen_num = text_curline / window_len + 1;
	time(&secs);
	sprintf(where_buf, "Screen %d of %d (%d lines)",
		screen_num, screens, text_numline);

	move(0, 0), printw("Public Information Prototype V 0.4  %45s", ctime(&secs));
	move(1, 0), printw("%-50.50s %29.29s", Heading, where_buf);
	move(2, 0);
	for (i = 0; i < COLS; i++)
		addch('=');
	for (i = numhead = 0; i < MAX_TXT_HEAD; i++) {
		if (*text_heading[i]) {
			move(WIN_TOP + numhead, 0);
			addstr(text_heading[i]), clrtoeol();
			numhead++;
		}
	}
	for (linecnt = 0; linecnt < window_len; linecnt++) {
		char	*txtline;

		move(linecnt + WIN_TOP + numhead, 0);

		if (display_func)
			txtline = (*display_func)(linecnt + text_curline);
		else
			txtline = "<no active displayer!>";

		addstr(txtline);
		clrtoeol();
		if (linecnt % 4 == 0)
			refresh(), flush();

		if (so_len && linecnt + text_curline == so_line) {
			move(linecnt + WIN_TOP + numhead, so_col);
			standout();
			printw("%.*s", so_len, &txtline[so_col]);
			standend();
			so_len = 0;
			move(linecnt + WIN_TOP + numhead + 1, 0);
		}
	}
	clrtobot();
	move(LINES - 3, 0);
	for (i = 0; i < COLS; i++)
		addch('=');
	if (*ErrMsg)
		move(LINES - 3, 0), printw("===[ %s ]", ErrMsg);
	mvaddstr(LINES - 1, 0, *Msg ? Msg : help_msg);
	*ErrMsg = *Msg = '\0';

}

void
text_based_pips_init()
{
	if (!curses_active)
		initscr(), curses_active++;
	else
		clearok(stdscr, TRUE);
	cbreak(), noecho(), nonl();
}

void
text_based_pips_quit()
{
	clear();
	refresh();
	endwin();
}

#define	STDOUT_BUFLEN	1024		/* Bufferd output buffer */

extern int	input_pending;

void
text_based_pips_loop()
{
	char	stdout_buf[STDOUT_BUFLEN];
	extern char	*getinput();

	/*
	 * This setbuffer is what makes the screen blindinly fast.
	 * (Well, only when the buflen is bigger than average screen size)
	 * Without it, the screen is updated a line at a time instead
	 * of ususally all at once (or whenever the buffer is full).
	 */

	setbuffer(stdout, stdout_buf, STDOUT_BUFLEN);
	
	for (;;) {
		char	cmd_line[128];

		/*
		 * Don't do display output if pending input.  This makes
		 * for interruptable re-display, which makes things much
		 * faster in the case of say 2 seconds of auto-repeat on
		 * the down arrow.
		 */

		if (!input_pending) {
			display_screen();
			mvaddstr(LINES - 2, 0, "Enter a command: ");
			refresh(), flush();
		}

		parse_cmd(getinput(cmd_line));
	}

}

/*
 * The following ins a kludge, and needs to be implemented in some nice,
 * buffered manner so that infinte length files shouldn't be a problem.
 */

static char	text_buffer[4096][128];
static int	txtfile_numline;

static char *
text_line(lineno)
int	lineno;
{
	if (lineno >= txtfile_numline)
		return "~";

	return text_buffer[lineno];
}

load_text_with_file(filename)
char	filename[];
{
	FILE	*fp;
	int	line = 0, len;

	clear_text_headings();
	fp = fopen(filename, "r");
	if (!fp) {
		error(filename), flush();
		return;
	}
	while (fgets(text_buffer[line], 128, fp)) {
		if (*text_buffer[line] == '#')		/* Really do this? */
			continue;
		text_buffer[line][127] = '\0';
		len = strlen(text_buffer[line]);
		if (text_buffer[line][len - 1] == LF)
			text_buffer[line][len - 1] = '\0';
		line++;
	}
	fclose(fp);
	display_set(txtfile_numline = line, text_line);
}
