/*
 *	VISUAL.C
 *
 *	Atari console VT52 driving code.
 *
 *	The screen is divided into two regions.  The bottom 4 lines are used
 *	for scrolling textual messages, filename input, error messages etc.
 *	The top 20 lines are used for screen-oriented displays.
 *
 *	The top region is referred to as the display,
 *	the bottom region is the monologue box.
 *	One line between the two regions is left blank.
 *
 *	functions v_* are used for all line-oriented requests, but multi-line
 *		operations only affect the display area.
 *	functions m_* operate on the monologue box.
 *	Unprefixed functions which deal with the whole screen are private.
 */

#include <osbind.h>
#define CON 2

#define SCR_LINES 25	/* depth of screen */
#define SCR_COLUMNS 80	/* width of screen */
#define DISP_LINES 20	/* how many lines are used for top bit */
#define DISP_COLUMNS SCR_COLUMNS
#define MONO_LINES (SCR_LINES - DISP_LINES - 1)

#define outc(c) Bconout(CON, c)

/* routines which really do what they say to the whole screen */
static moveto(), clear(), clrdown(), delline();

v_init()
{
	register int i;

	v_curoff();
	clear();
	/* draw a nice line between the regions */
	moveto(DISP_LINES, 0);
	for (i=0; i<SCR_COLUMNS; i++) outc('-');
}

/* what to do to clear up */
v_quit()
{
	v_curon();
}

/* The real clear screen */
static
clear()
{
	v_str("\033H\033J");
}

/* clear the display region */
v_clear()
{
	register int row;

	for (row = 1; row < DISP_LINES; row++) {
		moveto(row, 0);
		v_clreoln();
	}
}

/* The Real cursor-move */	
static
moveto(row, col)
{
	outc('\033');
	outc('Y');
	outc(row + ' ');
	outc(col + ' ');
}

/* Fake cursor move which does bounds checking */
v_moveto(row, col)	/* 0..size-1 */
{
	if (col < 0 || col >= DISP_COLUMNS || row < 0 || row >= DISP_LINES) {
		error("OOPS(%d, %d)" , col, row);	/* recursive? */
		return;
	}
	moveto(row, col);
}	

v_clreoln()
{
	outc('\033');
	outc('K');
}

static
delline()
{
	outc('\033');
	outc('M');
}

v_putc(ch)
{
	outc(ch);
}

v_str(str)
char *str;
{
	register char *cp;

	for (cp=str; *cp != '\0'; ) outc(*cp++);
}

v_strn(str, n)
char *str;
{
	register int i;

	for (i=0; i<n; i++) outc(str[i]);
}

v_printf(fmt, a1, a2, a3, a4)
char *fmt;
{
	char buf[SCR_COLUMNS + 1];	/* message too long? BOOM! */
	sprintf(buf, fmt, a1, a2, a3, a4);
	v_str(buf);
}

v_2hex(n)
{
	static char hexdigit[16] = {
		'0', '1', '2', '3', '4', '5', '6', '7',
		'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
	};

	outc(hexdigit[n>>4&15]);
	outc(hexdigit[n&15]);
}

/* turn the text cursor off */
v_curoff()
{
	xbios(21, 0, 0);
}

/* turn the text cursor on */
v_curon()
{
	xbios(21, 1, 0);	/* I don't know what default flash rate is */
}

/* printf for monologue box */
m_printf(fmt, arg1, arg2, arg3, arg4)
char *fmt;
{
	char buf[81];
	sprintf(buf, fmt, arg1, arg2, arg3, arg4);
	m_str(buf);
}

/*
 *	Display a one-line message in the bottom window
 */
m_str(str)
char *str;
{
	v_begin();

	/* scroll the bottom window */
	moveto(DISP_LINES+1, 0);
	delline();

	/* print message on the last line of the screen */
	moveto(SCR_LINES-1, 0);
	v_str(str);
	
	v_end();
}

/*
 * Stuff to do interactive line input for (eg) filenames.
 * We need to do clever things if midi data arrives in the middle of a line.
 * If this happens, need to turn cursor off for the duration of possible
 * screen update due to midi info, and restore the cursor position afterwards.
 */
static int inputting = 0;	/* are we in the middle of line input? */
static int v_hpos;		/* position of cursor on input line */

/* Put a line (probably a menu) up on the top line */
d_print(str)
char *str;
{
	moveto(0, 0);
	v_str(str);
}

/* Print the prompt on the top line and read a line with echo */
d_input(prompt, buf, len)
char *prompt;		/* prompt to put out, may be "" */
char *buf;		/* where to put the input line */
int len;		/* size of buffer */
{
	register int i;		/* indexes where to put next char in buf */
	register int ch;	/* current input char */
	static v_midi();	/* function to call if midi sata arrives */

	moveto(0, 0);
	v_clreoln();
	v_str(prompt);
	v_hpos = strlen(prompt);
	inputting = 1;
	
	v_curon();	/* turn the text cursor on */
	
	for (i=0; (ch = mgetchar()) != '\r'; ) {

		/* Allow the poor souls to delete, at least.  Tabs screw up. */
		switch (ch) {
		case '\t':
			bell();
			continue;

		case '\b':
		case '\177':
			if (i > 0) {
				v_str("\b \b");
				i--;
				v_hpos--;
			} else {
				bell();	/* Can't delete past start of line */
			}
			continue;
		}

		if (i >= len-1) {	/* allow space for \0 */
			bell();		/* Bling! */
			error("Input line too long (%d chars max).", len-1);
			continue;
		}

		v_putc(ch);		/* echo it */
		buf[i++] = ch;
		v_hpos++;
	}

	buf[i] = '\0';
	v_curoff();
	inputting = 0;
}

/* v_begin is called before each screen-updating sequence */
v_begin()
{
	if (inputting) v_curoff();
}

/* v_end is called after each screen update */
v_end()
{
	if (inputting) {
		moveto(0, v_hpos);
		v_curon();
	}
}
