/*
 *  Module	curses		(Hopefully) machine-independant curses library.
 *  Author	larry gensch, ESQ
 *
 *  Note:   The inspiration for this module was an article by
 *	    Allen I. Holub in Dr. Dobbs Journal C Chest column.
 *	    Extensive modifications were made by larry gensch
 *	    to make the module conform to System V curses.
 *
 *  Copyright (c) 1987, 1988, 1989
 *	by Larry Gensch  /  104 Lowell Road  /  Salem, NH  03079
 *
 *  This code may be included in any work, public or private, with the
 *  exception of creating a commercial curses-compatible subroutine
 *  library.  (In other words, use the code all you want, but please don't
 *  rip off the author by reselling this code as your own).
 *
 *  If you make modifications to this code (specifically, enhancements that
 *  are compatible with System V.x curses), please send them to larry gensch at
 *  the address above to be considered for inclusion in subsequent releases.
 *  Any machine specific implementation modules (similar to v-msdos.c) for
 *  other machines are welcomed.
 */

#include "curses.h"
#include <ctype.h>
#include <stdlib.h>

/****
* There is no vsscanf function in ANSI standard, so it can't be relied
* on.  Define VSSCANF to the appropriate function name if it is supplied
* with the compiler.  If VSSCANF is not defined, the scanw family of calls
* will not be included in compilation.
****/

#ifdef THINK_C
#	define VSSCANF _vsscanf
#endif

#ifdef __WATCOMC__
#	define VSSCANF vsscanf
#endif

/*
 * EXPORT is used to show entry points into this module.  Simply grep
 * using the pattern '^EXPORT' to list them out.
 */

#ifndef EXPORT
#define EXPORT
#endif

/*
 * Exported variables (accessible by user programs):
 */

static char prntw_buf[512];

EXPORT int	COLS			= 0;
EXPORT int	LINES			= 0;
EXPORT WINDOW*	stdscr;
EXPORT char		*_curses_prntw = prntw_buf;
EXPORT unsigned	_curses_prntw_size	= sizeof(prntw_buf);
EXPORT int		_curses_tab_wid = 8;

static bool	Echo	= TRUE;		/* echo()/noecho() state	*/
static bool	Cbreak	= FALSE;	/* cbreak()/nocbreak() state	*/
static bool	Nl	= TRUE;			/* nl()/nonl() state		*/
static bool	Endwin	= TRUE;		/* isendwin() state		*/
static bool	Raw	= FALSE;		/* raw()/noraw() state		*/
static chtype	Kbbuf[256];		/* Keyboard input buffer	*/
static chtype	*Kbptr = Kbbuf;	/* Buffer pointer		*/
static int	Kbcount = 0;		/* Nbr characters in buffer	*/

static chtype ungot_char = -1;	/* Character pushed back by ungetch. */

/****
* The following variables are used to communicate between waddch
* and get_input (wgetch) when echo is on:
****/

static int waddch_scroll_cnt;	/* Number of lines window was scrolled up in get_input call. */
static int waddch_end_of_window_flag;	/* Set to TRUE if end of window reached and cannot scroll. */
static int waddch_allow_backspace_up;	/* Set to TRUE to allow backspace character to go up a line. */

/*
 * initscr() - initialize the screen and curses internal variables.
 *
 * Returns a pointer to stdscr if successful, otherwise prints a message
 * to stderr and exits.
 */

EXPORT WINDOW	*initscr	(void)
{
    if (! Endwin)
    	return NULL;

    if (! CURS_INIT_FN()) {
#ifdef THINK_C
		maccur_alert_msg("curses: error in terminal initialization.");
#else
		fputs("curses: error in terminal initialization.\n", stderr);
#endif
		exit(1);
    }

    if (LINES == 0)
		LINES = 24;
    if (COLS == 0)
		COLS = 80;

    stdscr = newwin(0,0,0,0);
    if (stdscr == NULL) {
#ifdef THINK_C
		maccur_alert_msg("curses: cannot allocate stdscr window.");
#else
		fputs("curses: cannot allocate stdscr window.\n", stderr);
#endif
		abort();
    }

    Endwin = FALSE;

    return stdscr;
}

/*
 * endwin() - reset terminal into previous state.  Required before any call to
 * system() or exit().
 */

EXPORT int	endwin		(void)
{
    if (stdscr == NULL)
		return ERR;

    Endwin = TRUE;

    return CURS_END_FN();
}

/*
 * isendwin() - determine if endwin() has been called without any subsequent
 * calls to wrefresh().
 */

EXPORT bool	isendwin	(void)
{
    return Endwin;
}

/* -------------------------------------------- */
/*             Window manipulation              */
/* -------------------------------------------- */

/*
 * wrefresh() - Write window to terminal.  Returns ERR if an error occurs.
 */

EXPORT int	wrefresh	(WINDOW *win)
{
    if (win != NULL && (win->_flags & _ISPAD) == 0) {
		Endwin = FALSE;
	
		if (win->_flags & (_WINCHANGED | _WINMOVED)) {
		    win->_flags &= ~(_WINCHANGED | _WINMOVED);
		    return CURS_REFRESH_FN(win, TRUE);
		}
		return OK;
    }

    return ERR;
}

/*
 * wnoutrefresh() - Update virtual terminal image (physical image updated by
 * doupdate()).
 */

EXPORT int	wnoutrefresh	(WINDOW *win)
{
    if (win != NULL && (win->_flags & _ISPAD) == 0) {
		if (win->_flags & (_WINCHANGED | _WINMOVED))
		{
		    win->_flags &= ~(_WINCHANGED | _WINMOVED);
		    return CURS_REFRESH_FN(win, FALSE);
		}
		return OK;
    }

    return ERR;
}

/*
 * pad_map: Used by prefresh/pnoutrefresh to determine  what part of
 *			pad to display.  Returns ERR on bad parameters.  Sets _WINMOVED
 *			flag if pad is not mapped to same location as previous call.
 */

static int pad_map		(WINDOW *pad, int pminy, int pminx,
						 int sminy, int sminx, int smaxy, int smaxx)
{
	int pmaxx, pmaxy;

	pminy = MAX(pminy, 0);
	pminx = MAX(pminx, 0);
	sminy = MAX(sminy, 0);
	sminx = MAX(sminx, 0);
	if (sminx >= smaxx && sminy >= smaxy) return ERR;

	pmaxx = pminx + (smaxx - sminx + 1);
	pmaxy = pminy + (smaxy - sminy + 1);
	pmaxx = MIN(pmaxx, pad->_maxx);
	pmaxy = MIN(pmaxy, pad->_maxy);
	
	if (sminy != pad->_begy || sminx != pad->_begx ||
			pminy != pad->_pmap_orgy || pminx != pad->_pmap_orgx ||
			pmaxy != pad->_pmap_maxy || pmaxx != pad->_pmap_maxx)
	{
		pad->_flags |= _WINMOVED;
		pad->_begy = sminy;
		pad->_begx = sminx;
		pad->_pmap_orgy = pminy;
		pad->_pmap_orgx = pminx;
		pad->_pmap_maxy = pmaxy;
		pad->_pmap_maxx = pmaxx;
	}

	return OK;
}

/*
 * prefresh() - Map and write pad to terminal.  Returns ERR if an error occurs.
 */

EXPORT int	prefresh	(WINDOW *pad, int pminy, int pminx,
						 int sminy, int sminx, int smaxy, int smaxx)
{
    if (pad != NULL && (pad->_flags & _ISPAD))
    {
		Endwin = FALSE;

		if (pad_map(pad, pminy, pminx, sminy, sminx,
									smaxy, smaxx) == ERR)
			return ERR;

		if (pad->_flags & (_WINCHANGED | _WINMOVED))
		{
		    pad->_flags &= ~(_WINCHANGED | _WINMOVED);
		    return CURS_REFRESH_FN(pad, TRUE);
		}
		return OK;
    }

    return ERR;
}

/*
 * pnoutrefresh() - Update virtual terminal image (physical image updated by
 * doupdate()).
 */

EXPORT int	pnoutrefresh	(WINDOW *pad, int pminy, int pminx,
						 int sminy, int sminx, int smaxy, int smaxx)
{
    if (pad != NULL && (pad->_flags & _ISPAD))
    {
		if (pad_map(pad, pminy, pminx, sminy, sminx,
									smaxy, smaxx) == ERR)
			return ERR;

		if (pad->_flags & (_WINCHANGED | _WINMOVED))
		{
		    pad->_flags &= ~(_WINCHANGED | _WINMOVED);
		    return CURS_REFRESH_FN(pad, FALSE);
		}
		return OK;
    }

    return ERR;
}

/*
 * doupdate() - Update the physical terminal image.
 */

EXPORT int	doupdate	(void)
{
	Endwin = FALSE;
    return CURS_REFRESH_FN(NULL, TRUE);
}

/*
 * window_init() - Initialize window structure.
 */

static void window_init	(WINDOW *win, int nlines, int ncols, int beg_y, int beg_x)
{
    win->_cury		= 0;
    win->_curx		= 0;
    win->_maxy		= nlines;
    win->_maxx		= ncols;
   	win->_xdim		= ncols;
    win->_begy		= beg_y;
    win->_begx		= beg_x;
    win->_flags		= _WINCHANGED;
    win->_attrs		= 0;
    win->_clear		= FALSE;
    win->_leave		= FALSE;
    win->_scroll	= FALSE;
    win->_use_idl	= FALSE;
    win->_use_keypad	= FALSE;
    win->_use_meta	= FALSE;
    win->_nodelay	= FALSE;
    win->_firstch	= NULL;
    win->_lastch	= NULL;
    win->_notimeout	= FALSE;
    win->_need_idl	= FALSE;
    win->_tmarg		= 0;
    win->_bmarg		= nlines;
}

/*
 * newwin() - Create and return a pointer to a new window.
 */

EXPORT WINDOW*	newwin		(int nlines, int ncols, int beg_y, int beg_x)
{
    WINDOW	*win;
    chtype	*screen;
    int		i;

    if (nlines > LINES || ncols > COLS)
		return NULL;

    if (nlines < 1)
		nlines = LINES;

    if (ncols < 1)
		ncols = COLS;

    if ( (beg_y < 0 || beg_y >= LINES) ||	/* RZ: Was >= on x/y + beg compare to lines/cols */
	 (beg_x < 0 || beg_x >= COLS)	||
    	 (nlines + beg_y > LINES)	||
	 (ncols + beg_x > COLS) )
		return NULL;

    if ((win = calloc(1, sizeof(WINDOW))) == NULL)
		return NULL;

    if ((screen = calloc(nlines * ncols, sizeof(chtype))) == NULL) {
		free(win);
		return NULL;
    }

	window_init(win, nlines, ncols, beg_y, beg_x);
    win->_y = screen;

    for (i = 0; i < nlines * ncols; i++, screen++)
		*screen = ' ';

    return win;
}

/*
 * subwin() - Create and return a pointer to a new window.
 */

EXPORT WINDOW*	subwin		(WINDOW *parnt_win, int nlines, int ncols, int beg_y, int beg_x)
{
	WINDOW *win;

	if (parnt_win == NULL)
		return NULL;

    if (nlines < 1 || ncols < 1)
		return NULL;

	if ( (beg_y < parnt_win->_begy) ||
			(beg_x < parnt_win->_begx) ||
			(nlines + beg_y > parnt_win->_maxy + parnt_win->_begy) ||
			(ncols + beg_x > parnt_win->_maxx + parnt_win->_begx) )
		return NULL;

    if ((win = calloc(1, sizeof(WINDOW))) == NULL)
		return NULL;

	window_init(win, nlines, ncols, beg_y, beg_x);
	win->_flags |= _SUBWIN;
	win->_y = parnt_win->_y + ((beg_y - parnt_win->_begy) * parnt_win->_xdim +
								beg_x - parnt_win->_begx);
	win->_xdim = parnt_win->_maxx;

	return win;
}

/*
 * newpad() - Create and return a pointer to a new pad.
 */

EXPORT WINDOW*	newpad		(int nlines, int ncols)
{
    WINDOW	*pad;
    chtype	*screen;
    int		i;

    if ( nlines < 1 || ncols < 1 )
		return NULL;

    if ((pad = calloc(1, sizeof(WINDOW))) == NULL)
		return NULL;

    if ((screen = calloc(nlines * ncols, sizeof(chtype))) == NULL) {
		free(pad);
		return NULL;
    }

	window_init(pad, nlines, ncols, 0, 0);
    pad->_pmap_orgy = 0;
    pad->_pmap_orgx = 0;
    pad->_pmap_maxy = LINES;
    pad->_pmap_maxx = COLS;
    pad->_flags |= _ISPAD;
    pad->_y = screen;

    for (i = 0; i < nlines * ncols; i++, screen++)
		*screen = ' ';

    return pad;
}

/*
 * subpad() - Create and return a pointer to a pad inside another pad.
 */

EXPORT WINDOW*	subpad		(WINDOW *parnt_pad, int nlines, int ncols, int beg_y, int beg_x)
{
	WINDOW *pad;

	if (parnt_pad == NULL || (parnt_pad->_flags & _ISPAD) == 0)
		return NULL;

    if (nlines < 1 || ncols < 1)
		return NULL;

	if ( (beg_y < 0) ||
			(beg_x < 0) ||
			(nlines + beg_y > parnt_pad->_maxy) ||
			(ncols + beg_x > parnt_pad->_maxx) )
		return NULL;

    if ((pad = calloc(1, sizeof(WINDOW))) == NULL)
		return NULL;

	window_init(pad, nlines, ncols, beg_y, beg_x);
    pad->_pmap_orgy = parnt_pad->_pmap_orgy;
    pad->_pmap_orgx = parnt_pad->_pmap_orgx;
    pad->_pmap_maxy = parnt_pad->_pmap_maxy;
    pad->_pmap_maxx = parnt_pad->_pmap_maxx;
	pad->_flags |= (_SUBWIN | _ISPAD);
	pad->_y = parnt_pad->_y + (beg_y * parnt_pad->_xdim + beg_x);
	pad->_xdim = parnt_pad->_maxx;

	return pad;
}

/*
 * mvwin() - move window so that the upper left corner will be at the specified
 * position.
 */

EXPORT int	mvwin		(WINDOW *win, int y, int x)
{
    if (win == NULL || (win->_flags & _ISPAD))
		return ERR;

    if ( (x + win->_maxx > COLS)	||
    	 (y + win->_maxy > LINES) )	/* RZ -- Check was >=, eliminated OK mappings. */
		return ERR;

    win->_begx = x;
    win->_begy = y;
    win->_flags |= _WINMOVED;

    return OK;
}

/*
 * delwin() - delete the named window, freeing all memory associated with it.
 */

EXPORT int	delwin		(WINDOW *win)
{
    if (win == NULL)
		return ERR;

    free(win->_y);
    free(win);

	return OK;
}

/*
 * waddch() - Output the specified character to the specified window.
 *
 * Note:  The "unctrl" routine in the default: case below is
 *	  specific to the ASCII character set.
 *
 */

EXPORT int	waddch		(WINDOW *win, chtype ch)
{
    int		rval = OK;
    chtype c, attr;

    if (win == NULL)
		return ERR;

    win->_flags |= _WINCHANGED;

	waddch_scroll_cnt = 0;
	waddch_end_of_window_flag = FALSE;

	c = (ch & A_CHARTEXT);
	attr = (ch & A_ATTRIBUTES);
    switch (c | (ch & A_ALTCHARSET))
    {
		case '\b':
		    if (win->_curx > 0 || (waddch_allow_backspace_up && win->_cury > 0))
		    {
				win->_y[win->_cury*win->_xdim + --win->_curx] = ' ' | win->_attrs;
				if (win->_curx < 0)
				{
					win->_cury--;
					win->_curx = win->_maxx - 1;
				}
			}
		    else rval = ERR;
	
		    break;

		case '\t':
		    do {
				waddch(win, ' ' | attr);
		    } while (win->_curx % _curses_tab_wid &&
		    		 win->_curx > 0 && !waddch_end_of_window_flag);	/* Don't let tab spill into new line. */
		    break;
	
		case '\r':
		    win->_curx = 0;
		    break;
	
		default:
		    if ( ( (ch & A_ALTCHARSET) == 0) &&
					iscntrl(c))
			{
				waddch(win, '^' | attr);
				ch = ((c == 0x7f) ? '?' : c + 'A' - 1) | attr;
		    }
	
		    win->_y[win->_cury*win->_xdim+win->_curx] = ch | win->_attrs;
	
		    if (++(win->_curx) < win->_maxx)
				break;
	
		    /* fall through to newline */
	
		case '\n':
		    if (ch == '\n')
				wclrtoeol(win);

		    win->_curx = 0;
		    win->_cury++;

			if (win->_scroll && win->_cury >= win->_bmarg )
		    {
				if ((rval = scroll(win)) != ERR)
				{
					win->_cury = win->_bmarg - 1;
					waddch_scroll_cnt++;
				}
		    }
	
		    break;
    }

	if (win->_cury >= win->_maxy)
	{
		win->_cury = win->_maxy - 1;
		win->_curx = win->_maxx - 1;
		waddch_end_of_window_flag = TRUE;
		rval = ERR;
	}

    win->_flags |= _WINCHANGED;
    waddch_allow_backspace_up = FALSE;
    return rval;
}

/*
 * wechochar() - waddch() followed by refresh()
 */

EXPORT int	wechochar	(WINDOW *win, chtype ch)
{
    waddch(win,ch);
    return wrefresh(win);
}

/*
 * pechochar() - waddch() followed by prefresh()
 */

EXPORT int	pechochar	(WINDOW *pad, chtype ch);

EXPORT int	pechochar	(WINDOW *pad, chtype ch)
{
    waddch(pad,ch);
    return prefresh(pad, pad->_pmap_orgy, pad->_pmap_orgx,
								pad->_begy, pad->_begx,
								pad->_begy + pad->_pmap_maxy - pad->_pmap_orgy - 1,
								pad->_begx + pad->_pmap_maxx - pad->_pmap_orgx - 1);
}

/*
 * waddstr() - Write the characters in the string to the window.
 */

EXPORT int	waddstr		(WINDOW *win, char *str)
{
    int		rval = OK;

    while (*str)
		if (waddch(win, (chtype) *(str++)) == ERR)
		    rval = ERR;

    return rval;
}

/*
 * wattroff() - Turn off the specified attributes in the window.
 */

EXPORT int	wattroff	(WINDOW *win, chtype attrs)
{
    if (win == NULL)
		return ERR;

    win->_attrs &= ~(attrs & A_ATTRIBUTES);
    return OK;
}

/*
 * wattron() - Turn on the specified attributes in the window.
 */

EXPORT int	wattron		(WINDOW *win, chtype attrs)
{
    if (win == NULL)
		return ERR;

    win->_attrs |= (attrs & A_ATTRIBUTES);
    return OK;
}

/*
 * wattrset() - Set the specified attributes in the window.
 */

EXPORT int	wattrset	(WINDOW *win, chtype attrs)
{
    if (win == NULL)
		return ERR;

    win->_attrs = (attrs & A_ATTRIBUTES);
    return OK;
}

/*
 * wstandout() - Turn on standout mode.
 */

EXPORT int	wstandout	(WINDOW *win)
{
    return wattron(win, A_STANDOUT);
}

/*
 * wstandend() - Turn off all attributes.
 */

EXPORT int	wstandend	(WINDOW *win)
{
    return wattrset(win, A_NORMAL);
}

/*
 * beep() - Sound the audible alarm()
 */

EXPORT int	beep		(void)
{
    CURS_BEEP_FN(FALSE);
    return OK;
}

/*
 * flash() - Flash the screen (alternately, sound the alarm)
 */

EXPORT int	alarm		(void)
{
    CURS_BEEP_FN(TRUE);
    return OK;
}

/*
 * box() - Draw a box around the edge of the window.
 */

EXPORT int	box		(WINDOW *win, chtype vertch, chtype horch)
{
    chtype	ul, ur, ll, lr;
    int		i;
    int		ox, oy;
    bool	os;

    if (win == NULL)
		return ERR;

	if (horch == '\b' || vertch == '\b')				/* These could cause infinite loop. */
		return ERR;

    if (vertch == 0)
		vertch = ACS_VLINE;
    if (horch == 0)
		horch = ACS_HLINE;

    if (vertch == ACS_VLINE && horch == ACS_HLINE) {
		ul = ACS_ULCORNER;
		ur = ACS_URCORNER;
		ll = ACS_LLCORNER;
		lr = ACS_LRCORNER;
    }
    else
		ul = ur = ll = lr = horch;

    ox = win->_cury;
    oy = win->_curx;
    os = win->_scroll;

    wmove(win, 0, 0);
    waddch(win, ul);
    win->_scroll = FALSE;

    for (i = 2; i < win->_maxx; i++)
		waddch(win, horch);

    waddch(win, ur);

    for (i = 1; i < win->_maxy - 1; i++) {
		wmove(win, i, 0);
		waddch(win, vertch);
		wmove(win, i, win->_maxx-1);
		waddch(win, vertch);
    }

    mvwaddch(win, win->_maxy - 1, 0, ll);

    for (i = 2; i < win->_maxx; i++)
		waddch(win, horch);

    waddch(win, lr);

    win->_cury   = ox;
    win->_curx   = oy;
    win->_scroll = os;
    win->_flags |= _WINCHANGED;

    return OK;
}

/*
 * werase() - Erase a window.
 */

EXPORT int	werase		(WINDOW *win)
{
    int		i, i_rw;
    chtype	ch;
    chtype	*screen, *dst_ptr;

    if (win == NULL)
		return ERR;

    ch = ' ';
    screen = win->_y;
    i = win->_maxx * win->_maxy;

	for (i_rw = 0; i_rw < win->_maxy; i_rw++)			/* Have to do row-by-row because of possible subwin/pad. */
	{
		dst_ptr = screen + i_rw * win->_xdim;
		for (i = 0; i < win->_maxx; i++)
			*(dst_ptr + i) = ch;
	}

    win->_flags |= _WINCHANGED;
    return OK;
}

/*
 * wclear() - Clear the window (functionally identical to erase())
 */

EXPORT int	wclear		(WINDOW *win)
{
    return werase(win);
}

/*
 * wclrtobot() - Clear from cursor position to the bottom of the window.
 */

EXPORT int	wclrtobot	(WINDOW *win)
{
    int		i, i_rw;
    int		max;
    chtype	*dst_ptr;
    chtype	ch;

    if (win == NULL)
		return ERR;

    win->_flags |= _WINCHANGED;

    ch = ' ';

	for (i_rw = win->_cury; i_rw < win->_maxy; i_rw++)		/* Have to do row-by-row because of possible subwin/pad. */
	{
		dst_ptr = win->_y + i_rw * win->_xdim;
		for (i = (i_rw == win->_cury ? win->_curx : 0); i < win->_maxx; i++)
			*(dst_ptr + i) = ch;
	}


    return OK;
}

/*
 * wclrtoeol() - Clear from cursor position to end of line in window.
 */

EXPORT int	wclrtoeol	(WINDOW *win)
{
    int		i;
    chtype	*screen;
    chtype	ch;

    if (win == NULL)
		return ERR;

	if (win->_cury >= win->_maxy)
		return ERR;

    win->_flags |= _WINCHANGED;

    screen = win->_y + (win->_cury * win->_xdim);
    ch = ' ';

	for (i = win->_curx; i < win->_maxx; i++)
		*(screen + i) = ch;

    return OK;
}

/*
 * delch() - Delete character under cursor, moving all characters to end of
 * line in the window to the right one character.
 */

void *memmove(void *s1, const void *s2, size_t n);

EXPORT int	wdelch		(WINDOW *win)
{
    chtype	*dst_ptr;
    int n;

    if (win == NULL)
		return ERR;

    win->_flags |= _WINCHANGED;

	dst_ptr = win->_y + (win->_cury * win->_xdim + win->_curx);
	n = win->_maxx - win->_curx - 1;
	memmove(dst_ptr, dst_ptr + 1, n * sizeof(chtype));
	*(dst_ptr + n) = ' ' | win->_attrs;

    return OK;
}

/*
 * wdeleteln() - Delete the line containing the cursor, moving all subsequent
 * lines in the window up one line.
 */

void *memcpy(void *s1, const void *s2, size_t n);

EXPORT int	wdeleteln	(WINDOW *win)
{
    int		i, i_rw;
    chtype	*dst_ptr;
    chtype	ch = 0;

    if (win == NULL)
		return ERR;

    win->_flags |= _WINCHANGED;

	for (i_rw = win->_cury; i_rw < win->_maxy - 1; i_rw++)
	{
		dst_ptr = win->_y + (i_rw * win->_xdim);
		memcpy(dst_ptr, dst_ptr + win->_xdim, win->_maxx * sizeof(chtype));
	}

	dst_ptr = win->_y + ((win->_maxy - 1) * win->_xdim);
	for (i = 0; i < win->_maxx; i++)
		*(dst_ptr + i) = ch;

    return OK;
}

/*
 * winsch() - Insert a character at the cursor position in the window, moving
 * all subsequent characters (to end of line) to the right one character.
 */

EXPORT int	winsch		(WINDOW *win, chtype ch)
{
    chtype	*src_ptr;

    if (win == NULL)
		return ERR;

    win->_flags |= _WINCHANGED;

	src_ptr = win->_y + (win->_cury * win->_xdim + win->_curx);
	memmove(src_ptr + 1, src_ptr, (win->_maxx - win->_curx - 1) * sizeof(chtype));
	*src_ptr = ch | win->_attrs;

    return OK;
}

/*
 * winsertln() - Insert a blank line above current line. (had a bug)
 */

EXPORT int	winsertln	(WINDOW *win)
{
    int		i, i_rw;
    chtype	*dst_ptr;
    chtype	ch;

    if (win == NULL)
		return ERR;

    win->_flags |= _WINCHANGED;

    ch = ' ';

	for (i_rw = win->_cury + 1; i_rw < win->_maxy; i_rw++)
	{
		dst_ptr = win->_y + (i_rw * win->_xdim);
		memcpy(dst_ptr, dst_ptr - win->_xdim, win->_maxx * sizeof(chtype));
	}

	dst_ptr = win->_y + (win->_cury * win->_xdim);
	for (i = 0; i < win->_maxx; i++)
		*(dst_ptr + i) = ch;

    return OK;
}

/*
 * printw() - Print a printf()-formatted string onto stdscr.
 */

EXPORT int	printw		(char *fmt, ...)
{
    va_list args;

    va_start(args, fmt);
    vsprintf(_curses_prntw, fmt, args);
    va_end(args);

    return addstr(_curses_prntw);
}

/*
 * mvprintw() - Print a printf()-formatted string onto stdscr after positioning.
 */

EXPORT int	mvprintw	(int y, int x, char *fmt, ...)
{
    va_list args;

	move(y, x);

    va_start(args, fmt);
    vsprintf(_curses_prntw, fmt, args);
    va_end(args);

    return addstr(_curses_prntw);
}

/*
 * wprintw() - Print a printf()-formatted string onto a window.
 */

EXPORT int	wprintw		(WINDOW *win, char *fmt, ...)
{
    va_list args;

    va_start(args, fmt);
    vsprintf(_curses_prntw, fmt, args);
    va_end(args);

    return waddstr(win, _curses_prntw);
}

/*
 * mvwprintw() - Print a printf()-formatted string onto a window after positioning.
 */

EXPORT int	mvwprintw	(WINDOW *win, int y, int x, char *fmt, ...)
{
    va_list args;

	wmove(win, y, x);

    va_start(args, fmt);
    vsprintf(_curses_prntw, fmt, args);
    va_end(args);

    return waddstr(win, _curses_prntw);
}

/*
 * vwprintw() - Print a printf()-formatted string onto a window using a
 * variable argument list.
 */

EXPORT int	vwprintw	(WINDOW *win, char *fmt, va_list ap)
{
    va_list args;

    vsprintf(_curses_prntw, fmt, ap);

    return waddstr(win, _curses_prntw);
}

/*
 * scroll() - Scroll a window up one line.
 *	This is the same as wdeleteln on the top scrolling char.
 */

EXPORT int	scroll		(WINDOW *win)
{
    int		i, i_rw;
    chtype	*dst_ptr;
    chtype	ch;

    if (win == NULL)
		return ERR;

    if (!win->_scroll)
		return ERR;

    win->_flags |= _WINCHANGED;

    ch = ' ' | win->_attrs;

	for (i_rw = win->_tmarg; i_rw < win->_bmarg - 1; i_rw++)
	{
		dst_ptr = win->_y + (i_rw * win->_xdim);
		memcpy(dst_ptr, dst_ptr + win->_xdim, win->_maxx * sizeof(chtype));
	}

	dst_ptr = win->_y + ((win->_bmarg - 1) * win->_xdim);
	for (i = 0; i < win->_maxx; i++)
		*(dst_ptr + i) = ch;

    return OK;
}

/*
 * touchwin() - Pretend that the window has been modified since last refresh().
 */

EXPORT int	touchwin	(WINDOW *win)
{
    if (win == NULL)
		return ERR;

    win->_flags |= _WINCHANGED;

    return OK;
}

/*
 * touchline() - Functionally identical to touchwin()
 */

EXPORT int	touchline	(WINDOW *win, int start, int count)
{
    return touchwin(win);
}

/*
 * wmove() - Set the cursor position within a window.
 */

EXPORT int	wmove		(WINDOW *win, int ypos, int xpos)
{
    if (win == NULL)
		return ERR;

    if ( (xpos < 0 || xpos >= win->_maxx) ||
	 (ypos < 0 || ypos >= win->_maxy) )
		return ERR;

    win->_curx = xpos;
    win->_cury = ypos;

    return OK;
}

/*
 * copywin() - Move text from one window to another.
 */

EXPORT int copywin		(WINDOW *src_win, WINDOW *dst_win,
						 int sminy, int sminx, int dminy, int dminx,
						 int dmaxy, int dmaxx, bool overlay_flag)
{
	int nrows, nchars, i, j;
	chtype *src_ptr, *dst_ptr;

	sminy = MAX(sminy, 0);
	sminx = MAX(sminx, 0);
	dminy = MAX(dminy, 0);
	dminx = MAX(dminx, 0);

	if (sminy >= src_win->_maxy || sminx >= src_win->_maxx ||
			dminy >= dst_win->_maxy || dminx >= dst_win->_maxx ||
			dminy >= dmaxy || dminx >= dmaxx)
		return ERR;

    dst_win->_flags |= _WINCHANGED;

	nrows = MIN(dmaxy + 1, dst_win->_maxy) - dminy;
	nrows = MIN(nrows, src_win->_maxy - sminy);
	nchars = MIN(dmaxx + 1, dst_win->_maxx) - dminx;
	nchars = MIN(nchars, src_win->_maxx - sminx);
	src_ptr = src_win->_y + (sminy * src_win->_xdim) + sminx;
	dst_ptr = dst_win->_y + (dminy * dst_win->_xdim) + dminx;

	for (i = 0; i < nrows; i++)
	{
		if (overlay_flag)
		{
			for (j = 0; j < nchars; j++)
				if ((*src_ptr & A_CHARTEXT) != ' ')
					*(dst_ptr + j) = *(src_ptr + j);
		}
		else
			memmove(dst_ptr, src_ptr, nchars * sizeof(chtype));

		src_ptr += src_win->_xdim;
		dst_ptr += dst_win->_xdim;
	}

	return OK;
}

/*
 * overwrite()/overlay() - Copy one window onto another.  If either is
 *	a pad, the copy is done as if the two windows/pads have the same
 *	upper left corner.
 */

EXPORT int	overwrite	(WINDOW *src_win, WINDOW *dst_win)
{

	if ((src_win->_flags & _ISPAD) || (dst_win->_flags & _ISPAD))
		return copywin(src_win, dst_win, 0, 0, 0, 0,
									dst_win->_maxy - 1,
									dst_win->_maxx - 1,
									FALSE);
	else
		return copywin(src_win, dst_win, 0, 0,
									src_win->_begy - dst_win->_begy,
									src_win->_begx - dst_win->_begx,
									src_win->_maxy + src_win->_begy - dst_win->_begy - 1,
									src_win->_maxx + src_win->_begx - dst_win->_begx - 1,
									FALSE);
}

EXPORT int	overlay		(WINDOW *src_win, WINDOW *dst_win)
{

	if ((src_win->_flags & _ISPAD) || (dst_win->_flags & _ISPAD))
		return copywin(src_win, dst_win, 0, 0, 0, 0,
									dst_win->_maxy - 1,
									dst_win->_maxx - 1,
									TRUE);
	else
		return copywin(src_win, dst_win, 0, 0,
									src_win->_begy - dst_win->_begy,
									src_win->_begx - dst_win->_begx,
									src_win->_maxy + src_win->_begy - dst_win->_begy - 1,
									src_win->_maxx + src_win->_begx - dst_win->_begx - 1,
									TRUE);
}

/*
 * cbreak()/nocbreak() - Put the terminal into cbreak mode (ignored currently)
 */

EXPORT int	cbreak		(void)
{
    return (Cbreak = TRUE);
}

EXPORT int	nocbreak	(void)
{
    return (Cbreak = FALSE);
}

/*
 * echo()/noecho() - Echo characters as they are typed
 */

EXPORT int	echo		(void)
{
    return (Echo = TRUE);
}

EXPORT int	noecho		(void)
{
    return (Echo = FALSE);
}

/*
 * nl()/nonl() - CR -> NL mapping
 */

EXPORT int	nl		(void)
{
    return (Nl = TRUE);
}

EXPORT int	nonl		(void)
{
    return (Nl = FALSE);
}

/*
 * halfdelay() - ignored
 */

EXPORT int	halfdelay	(int tenths)
{
	return OK;
}

/*
 * intrflush() - Determines whether or not to flush input on interrupt.
 */

EXPORT int	intrflush	(WINDOW *win, bool flag)
{
    if (win == NULL)
	return ERR;
    win->_flags |= _FLUSH;
    return OK;
}

/*
 * keypad() - Turns on/off special key mapping.
 */

EXPORT int	keypad		(WINDOW *win, bool flag)
{
    if (win == NULL)
	return ERR;
    win->_use_keypad = flag;
    return OK;
}

/*
 * meta() - Turns on/off meta mapping.
 */

EXPORT int	meta		(WINDOW *win, bool flag)
{
    if (win == NULL)
	return ERR;
    win->_use_meta = flag;
    return OK;
}

/*
 * nodelay() - Turns wgetch() into a non-blocking call.
 */

EXPORT int	nodelay		(WINDOW *win, bool flag)
{
    if (win == NULL)
	return ERR;

    win->_nodelay = flag;
    return OK;
}

/*
 * notimeout() - Turn on/off escape sequence timing.
 */

EXPORT int	notimeout	(WINDOW *win, bool flag)
{
    if (win == NULL)
	return ERR;
    win->_notimeout = flag;
    return OK;
}

/*
 * raw()/noraw() - Return characters as they are received.
 */

EXPORT int	raw		(void)
{
    return (Raw = TRUE);
}

EXPORT int	noraw		(void)
{
    return (Raw = FALSE);
}

/*
 * get_input() - Get input from keyboard.  Does an implicit refresh of screen.
 */

static int	get_input		(WINDOW *win, chtype *chrs, int chrs_max,
							 bool cbrk_flag, bool raw_flag)
{
    chtype	c, save_attr;
    int c_pos, start_col, start_row, i;

	c_pos = 0;
	start_col = win->_curx;
	start_row = win->_cury;

	wrefresh(win);

    for (;;)
    {
		c = CURS_KBINP_FN(win, raw_flag, cbrk_flag);
	
		if (c == ERR)
		    return ERR;
	
		if (Nl && c == '\r')
		    c = '\n';

		if (cbrk_flag)
		{
		    *(chrs + c_pos++) = c;
		    if (Echo)
				waddch(win, c);
		}
		else
		{
			switch (c)
			{
				case '\b':				/* Backspace (delete previous character) key. */
					if (!raw_flag)		/* Raw mode will fall through to default case. */
					{
					    if (c_pos <= 0) {
							beep();
							break;
					    }
		
						if (Echo)
						{
						    waddch_allow_backspace_up = TRUE;
						    waddch(win, c);
						    if (iscntrl( (*(chrs + (c_pos - 1))) & A_CHARTEXT ))
						   	{
						    	waddch_allow_backspace_up = TRUE;
						    	waddch(win, c);
						    }
						}
					    c_pos--;
					    break;
					}

				case '\025':			/* ctrl-U => Clear input. */
					if (!raw_flag)		/* Raw mode will fall through to default case. */
					{
						if (Echo)
						{
							win->_curx = start_col;
							win->_cury = start_row;
							save_attr = win->_attrs;
							win->_attrs = 0;
							for (i = 0; i < c_pos; i++)
							{
								if ( ((*(chrs + i)) & A_CHARTEXT) == '\t')
									waddch(win, '\t');
								else
								{
									waddch(win, ' ');
									if (iscntrl((*(chrs + i)) & A_CHARTEXT))
										waddch(win, ' ');
								}
								if (waddch_end_of_window_flag) break;
							}
							win->_curx = start_col;
							win->_cury = start_row;
							win->_attrs = save_attr;
						}
						c_pos = 0;
						break;
					}

				default:
				    if (chrs_max != -1 && c_pos >= chrs_max)
				    {
						beep();
						break;
				    }
				    *(chrs + c_pos++) = c;
				    if (Echo)
				    {
				    	waddch(win, c);
				    	start_row -= waddch_scroll_cnt;
				    }
				    break;
			}
	    }
	    if (Echo)
	    	wrefresh(win);
		if (cbrk_flag || c == '\n' || c == '\r')
			return c_pos;
	}
}

/*
 * ungetch() - Push back a character to be returned by next
 *	call to wgetch or wgetstr.
 */

EXPORT int ungetch		(chtype ch)
{
	ungot_char = ch;

	return ch;
}

/*
 * wgetch() - Get character from keyboard.
 */

EXPORT int	wgetch		(WINDOW *win)
{
    int cnt;

	if (ungot_char != -1)
	{
		chtype ch = ungot_char;
		ungot_char = -1;
		return ch;
	}

	if (Kbcount <= 0)
	{
	    Kbptr = Kbbuf;

		if ((cnt = get_input(win, Kbbuf, sizeof(Kbbuf),
							 Cbreak, Raw)) == ERR || cnt == 0)
			return ERR;

		Kbcount = cnt;
	}

	Kbcount--;
	return *(Kbptr++);
}

/*
 * wgetstr() - Get a string from keyboard.  Input is terminated by return
 *	or newline (which is stripped from string and replaced with null termination).
 */

EXPORT int wgetstr		(WINDOW *win, char *str)
{
	int i, ungot_off, cnt, c;

	if (ungot_char != -1)
	{
		if ((*(str) = (ungot_char & A_CHARTEXT)) == '\n' || *str == '\r')
		{
			ungot_char = -1;
			*str = '\0';
			return OK;
		}
		ungot_off = 1;
		ungot_char = -1;
	}
	else ungot_off = 0;

	if (Kbcount <= 0)
	{
	    Kbptr = Kbbuf;

		if ((cnt = get_input(win, Kbbuf, sizeof(Kbbuf),
							 FALSE, FALSE)) == ERR || cnt == 0)
			return ERR;

		Kbcount = cnt;
	}

	for (i = 0; i < Kbcount - 1; i++)
		*(str + ungot_off + i) = Kbbuf[i] & A_CHARTEXT;

	if ((c = (Kbbuf[i] & A_CHARTEXT)) != '\n' && c != '\r')
		*(str + ungot_off + i++) = c;

	Kbcount = 0;
	*(str + ungot_off + i) = '\0';
	return OK;
}

#ifdef VSSCANF

/*
 * scanw() - Read a scanf()-formatted string from stdscr.
 */

EXPORT int	scanw		(char *fmt, ...)
{
    va_list args;
    int rtn;

	if (wgetstr(stdscr, _curses_prntw) == ERR)
		return ERR;

    va_start(args, fmt);
    rtn = VSSCANF(_curses_prntw, fmt, args);
    va_end(args);

    return rtn;
}

/*
 * mvscanw() - Read a scanf()-formatted string from stdscr after positioning.
 */

EXPORT int	mvscanw	(int y, int x, char *fmt, ...)
{
    va_list args;
    int rtn;

	move(y, x);

	if (wgetstr(stdscr, _curses_prntw) == ERR)
		return ERR;

    va_start(args, fmt);
    rtn = VSSCANF(_curses_prntw, fmt, args);
    va_end(args);

    return rtn;
}

/*
 * wscanw() - Read a scanf()-formatted string from a window.
 */

EXPORT int	wscanw		(WINDOW *win, char *fmt, ...)
{
    va_list args;
    int rtn;

	if (wgetstr(win, _curses_prntw) == ERR)
		return ERR;

    va_start(args, fmt);
    rtn = VSSCANF(_curses_prntw, fmt, args);
    va_end(args);

    return rtn;
}

/*
 * mvwscanw() - Read a scanf()-formatted string from a window after positioning.
 */

EXPORT int	mvwscanw	(WINDOW *win, int y, int x, char *fmt, ...)
{
    va_list args;
    int rtn;

	wmove(win, y, x);;

	if (wgetstr(win, _curses_prntw) == ERR)
		return ERR;

    va_start(args, fmt);
    rtn = VSSCANF(_curses_prntw, fmt, args);
    va_end(args);

    return rtn;
}

/*
 * vwscanw() - Read a scanf()-formatted string from a window using a
 * variable argument list.
 */

EXPORT int	vwscanw	(WINDOW *win, char *fmt, va_list ap)
{
    va_list args;

	if (wgetstr(win, _curses_prntw) == ERR)
		return ERR;

    return VSSCANF(_curses_prntw, fmt, ap);
}

#endif /* VSSCANF */

/*
 * flushinp() - Flush input queue
 */

EXPORT int	flushinp	(void)
{
	Kbcount = 0;
	Kbptr = Kbbuf;
 
 #ifdef CURS_FLUSHINP_FN
 	CURS_FLUSHINP_FN();
 #endif
 
	return OK;
}
