/*  skio.c                 (c) Steve Adam 1995         steve@netinfo.com.au
 *
 *      Screen and keyboard i/o routines for the ST, ST/e.
 *      
 *      This module supplies screen control functions similar to those
 *  allowed on vt100 (and hopefully other) terminals.  The objective is
 *  to make screen control features easy to use, and allow various
 *  terminal emulators (at least vt100) to be implemented easily.
 *
 *      VDI text and graphics text functions are used.
 *
 *      This initial version will only copy characters directly to the
 *  screen.  The initial version will also do single character I/O
 *  to the screen instead of strings (which would be faster).  This
 *  method might be OK permanently.
 *
 *      Later versions will write to `virtual' screens which might
 *  not be currently displayed physically.  (ie: hidden windows)
 */

#include <aes.h>
#include <vdi.h>
#include <tos.h>

#include <\include\drivers.h>

/* functions in skio.c          */
extern int16 scr_init(void);
extern void  scr_close(void);
extern void  scr_mvcursor(int16, int16);
extern void  scr_curson(void);
extern void  scr_cursoff(void);
extern void  scr_conout(char);
extern void  scr_conws(char *);
extern void  scr_ldel(int16);
extern void  scr_setwrap(int16);
extern void  scr_clear(void);
extern void  scr_setsregn(int16, int16);
extern void  scr_save(void);
extern void  scr_restore(void);
extern void  scr_setgrend(int16); 
extern void  scr_getcurpos(int16 *, int16 *);
extern void  scr_getcurmax(int16 *, int16 *);
extern void  scr_lclear(int16);
extern void  scr_bell(void);
extern long  scr_conin(int16);
extern int16 scr_gets(char *, int16);
extern int16 scr_conis(void);

extern long  scr_getkey(int16);
extern int16 scr_gets(char *, int16);
extern int16 scr_conis(void);

#define BDEV_CON 2
#define SCRE_REVERSE  256   /* This is NOT a VDI attribute  */

int16 scr_xmax, scr_ymax; /* Max x/y char cells (ie: Window size in chars)  */

/*  Current attribute variables */
int16 scr_reverse;
int16 scr_cx;
int16 scr_cy;

/*  Saved attribute values      */
int16 saved_reverse;
int16 saved_cx;
int16 saved_cy;

static void Bconws(char *s)
{
	while (*s)
		Bconout(BDEV_CON, *s++);
}

/* Global screen and terminal modes */
int16 scr_win_reverse;  /* Whole window reversed?   */
int16 scr_wrapmode;     /* Wrap at eol??            */
int16 scr_regntop;      /* Top of scroll region     */
int16 scr_regnbot;      /* Bottom of scroll region  */
int16 scr_origin;       /* Origin.  FALSE = screen, TRUE = scroll region    */
int16 scr_iscursor;     /* TRUE if curser is on     */

/*  init_scr()  Initialise the screen.  For this version, open the
 *              virtual screen workstation, clear attribute settings
 *              set up character cell sizes and offsets.  Also set the
 *              background/foreground colours for text I/O;
 *
 *              An appl_init() for AES is also called...
 */

int16 scr_init()
{
    scr_xmax = 80;
    scr_ymax = 25;

    scr_regntop = 0;
    scr_regnbot = scr_ymax;
    scr_origin = FALSE;

    /* Clear effects. etc.  */
    scr_reverse = FALSE;
    scr_cx = 0;
    scr_cy = 0;

    saved_reverse = FALSE;
    saved_cx = 0;
    saved_cy = 0;

    scr_win_reverse = FALSE;    /* Ignore this for now  */
    scr_wrapmode = TRUE;
	Bconws("\033e");
	Bconws("\033w");
	scr_iscursor = TRUE;

    return (0); /* Assume no error, for now */
}

void scr_close()
{
	/* Just a stub... */
}

/* Cursor notes.  For the time being, use the built in text cursor.
 *                ie: use vt52 codes to position it, etc.
 */

void scr_mvcursor(y, x)
int16 y, x;
{
    char *cset = "\033Y  ";

    if (x < 0)
        x = 0;
    if (x > scr_xmax)
        x = scr_xmax;

    if (y < 0)
        y = 0;
    if (y > scr_ymax)
        y = scr_ymax;

    cset[2] = ' ' + y;
    cset[3] = ' ' + x;

    Bconws(cset);

    scr_cx = x; /* Set our internal ptr's too   */
    scr_cy = y;
}

/* Best to have macro's for internal use */
#define CURSON   if (scr_iscursor) Bconws("\033e")
#define CURSOFF  if (scr_iscursor) Bconws("\033f")

void scr_curson()
{
	if (!scr_iscursor) {
		Bconws("\033e");
		scr_iscursor = TRUE;
	}
}

void scr_cursoff()
{
	if (scr_iscursor) {
		Bconws("\033f");
		scr_iscursor = FALSE;
	}
}


/*  scr_conout()    Output a single character.
 *                  We don't need to worry about modes etc.  We only
 *                  need to keep track of the cursor position.
 *
 *                  Output all special characters except \r\n\t\b
 *                  Translate \t to a single space for now.
 */


void scr_conout(c)
register char c;
{
    register char *chout = " ", *action = "\033   ";
    static int is_reverse = FALSE;

    if (c == '\r') {
        scr_mvcursor(scr_cy, 0);    /* Same line, column 0  */
        return;
    }
    if (c == '\n') {
        if (scr_cy >= scr_regntop && scr_cy < scr_regnbot)
            scr_mvcursor(scr_cy + 1, scr_cx);

        else if (scr_cy == scr_regnbot) {
			action[1] = 'Y';	/* Cursor to top of scroll region */
			action[2] = ' ' + scr_regntop;
			action[3] = ' ';
			Bconws(action);

			action[1] = 'M';	/* delete line.. */
			action[2] = '\0';
			Bconws(action);

			action[1] = 'Y';	/* Cursor to bottom of scroll region */
			action[2] = ' ' + scr_regnbot;
			action[3] = ' ';
			Bconws(action);

			action[1] = 'L';	/* insert line.. */
			action[2] = '\0';
			Bconws(action);
		}
        /* If not in the scrolling region, then ignore linefeed     */

        return;
    }
    if (c == '\b') {
        scr_mvcursor(scr_cy, scr_cx - 1);
        return;
    }
    if (c == 7) {   /* BELL make a noise, but don't echo... */
        scr_bell();
        return;
    }
    if (c == '\t') {
        c = ' ';        /* Need a tab system later  */
    }

    /* Now just output the char */

    *chout = c;

    CURSOFF;

    if (scr_reverse) {
		if (!is_reverse) {
			Bconws("\033p");
			is_reverse = TRUE;
		}
	}
    else {
		if (is_reverse) {
			Bconws("\033q");
			is_reverse = FALSE;
		}
	}

	Bconws(chout);
    CURSON;

    /* Check for end of line and wrap   */
    /* if wrap is required, just recurse here with \r\n */
    if (scr_wrapmode && scr_cx == scr_xmax) {
        scr_conout('\r');
        scr_conout('\n');
    }
    else {
        scr_mvcursor(scr_cy, scr_cx + 1);
    }
}

/* scr_conws()  Output a string by repeated calls to scr_conout()
 */

void scr_conws(s)
char *s;
{
    while (*s)
        scr_conout(*s++);
}

/* Clear a line.  If current line, set cursor to first column
 *
 *                If -ve line number is passed, clear the current line
 */

void scr_lclear(l)
register int16 l;
{
	char *action = "\033l";

    if (l < 0)
        l = scr_cy;
    if (l > scr_ymax)
        l = scr_ymax;

	Bconws(action);

    if (l == scr_cy)
        scr_mvcursor(l, 0);
}

void scr_setwrap(flag)
int16 flag;
{
    scr_wrapmode = flag;
}

void scr_clear()
{
    scr_mvcursor(0, 0);
    Bconws("\033E");
}

void scr_setsregn(top, bot)
int16 top, bot;
{
    if (top < 0)
        top = 0;
    if (bot > scr_ymax)
        bot = scr_ymax;

    if (top >= bot)
        return;

    scr_regntop = top;
    scr_regnbot = bot;
}

void scr_save()
{
    saved_reverse = scr_reverse;
    saved_cx = scr_cx;
    saved_cy = scr_cy;
}

void scr_restore()
{
    scr_reverse = saved_reverse;
    scr_cx = saved_cx;
    scr_cy = saved_cy;
    scr_mvcursor(scr_cy, scr_cx);
}

void scr_setgrend(grend)
int16 grend;
{
    if (grend & SCRE_REVERSE)
        scr_reverse = TRUE;
	else
		scr_reverse = FALSE;

}

void scr_getcurpos(y, x)
int16 *y, *x;
{
    *y = scr_cy;
    *x = scr_cx;
}

void scr_getcurmax(y, x)
int16 *x, *y;
{
    *y = scr_ymax;
    *x = scr_xmax;
}

void scr_bell()
{
    Bconout(BDEV_CON, 7);
}

/*  Keyboard routines
 *
 *  These are necessary so that character entry with echo properly records
 *  cursor position.
 */

long scr_conin(int16 echo_flag)
{
    register long key;

    while (!Bconstat(BDEV_CON)) /* This is only for single client version!! */
        ;

    key = Bconin(BDEV_CON);

    if (echo_flag) {
        if ((key & 0xff) == '\r' || ((key & 0xff) == '\n')) {
            scr_conout('\r');
            scr_conout('\n');
        }
        else {
            scr_conout((char)(key & 0xff));
        }
    }

    return (key);
}

int16 scr_gets(char *s, int16 max_len)
{
    register int16 len = 0;
    register long key;

    while (len < max_len) {
        key = scr_conin(FALSE);

        if ((key & 0xff) == '\r' || ((key & 0xff) == '\n'))
            break;

        if ((key & 0xff) == '\b') {
            if (len > 0) {
                scr_conws("\b \b");
                len -= 1;
            }
        }
        else if ((key & 0xff) == '\t')
            key = ' ';

        else if ((key & 0xff) != 0) {
            scr_conout((char)(key &0xff));
            s[len] = key & 0xff;
            len += 1;
        }
    }
    s[len] = '\0';

    scr_conout('\r');
    scr_conout('\n');

    return (len);
}

int16 scr_conis(void)
{
    if (Bconstat(BDEV_CON))
        return (TRUE);
	else
		return (FALSE);
}
