/*	Copyright (c) 1988 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#pragma ident	"@(#)idlok.c	1.11	97/08/22 SMI"	/* SVr4.0 1.19	*/

/*LINTLIBRARY*/

#include	<stdlib.h>
#include	"curses_inc.h"


/* Functions to make use of insert/delete line caps */

#define	scrco	COLS

typedef	struct
	{
	    int		_wy,	/* matching lines */
			_sy;
	} IDST;
static	IDST	*sid, *eid;		/* list of idln actions */
static	int	scrli,			/* screen dimensions */
		cy, cx;			/* current cursor positions */
static	bool	didcsr;			/* scrolling region was used */
static	int	_use_idln(void), _set_idln(void);
static	void	_do_idln(int, int, int, int);

/* Set insert/delete line mode for win */

idlok(WINDOW *win, bool bf)
{
	_useidln = _use_idln;
	_setidln = _set_idln;

	SP->yesidln = (delete_line || parm_delete_line ||
	    (change_scroll_region && (parm_index || scroll_forward))) &&
	    (insert_line || parm_insert_line ||
	    (change_scroll_region && (parm_rindex || scroll_reverse)));

	win->_use_idl = bf;
	return (OK);
}

/*
 * Set the places to do insert/delete lines
 * Return the start line for such action.
 */

static int
_set_idln(void)
{
	/*
	* The value we want to return is the lower line
	* number of the top-most range.
	*
	* If there is more than one range of lines on which
	* we're operating, _find_idln will get called more
	* then once; we need to search all the IDST for the
	* desired return value.
	*/
	{
		IDST *idp;
		int rval = scrli;

		for (idp = sid; idp != eid; idp++) {
			int tmp;

			if ((tmp = _MIN(idp->_wy, idp->_sy)) < rval)
				rval = tmp;
		}
		return (rval);
	}
}

/* Use hardware line delete/insert */

static int
_use_idln(void)
{
	int	tsy, bsy, idn, dir, nomore;
	IDST	*ip, *ep, *eip;

	cy = curscr->_cury;
	cx = curscr->_curx;
	didcsr = FALSE;

	/* first cycle do deletions, second cycle do insertions */
	for (dir = 1; dir > -2; dir -= 2) {
		if (dir > 0) {
			ip = sid;
			eip = eid;
		} else {
			ip = eid - 1;
			eip = sid - 1;
		}

		nomore = TRUE;
		while (ip != eip) {
			/* skip deletions or insertions */
			if ((dir > 0 && ip->_wy > ip->_sy) ||
			    (dir < 0 && ip->_wy < ip->_sy)) {
				nomore = FALSE;
				ip += dir;
				continue;
			}

			/* find a contiguous block */
			for (ep = ip+dir; ep != eip; ep += dir)
				if (ep->_wy != (ep - dir)->_wy + dir ||
				    ep->_sy != (ep - dir)->_sy + dir) {
				    break;
			}
			ep -= dir;

			/* top and bottom lines of the affected region */
			if (dir > 0) {
				tsy = _MIN(ip->_wy, ip->_sy);
				bsy = _MAX(ep->_wy, ep->_sy) + 1;
			} else {
				tsy = _MIN(ep->_wy, ep->_sy);
				bsy = _MAX(ip->_wy, ip->_sy) + 1;
			}

			/* amount to insert/delete */
			if ((idn = ip->_wy - ip->_sy) < 0)
				idn = -idn;

			/* do the actual output */
			_do_idln(tsy, bsy, idn, dir == -1);

			/* update change structure */
			(void) wtouchln(_virtscr, tsy, bsy - tsy, -1);

			/* update screen image */
			/*LINTED*/
			curscr->_tmarg = (short) tsy;
			curscr->_bmarg = bsy - 1;
			/*LINTED*/
			curscr->_cury = (short) tsy;
			(void) winsdelln(curscr, dir > 0 ? -idn : idn);
			curscr->_tmarg = 0;
			curscr->_bmarg = scrli - 1;

			/* for next while cycle */
			ip = ep + dir;
		}

		if (nomore)
			break;
	}

	/* reset scrolling region */
	if (didcsr) {
		_PUTS(tparm_p2(change_scroll_region, 0, scrli - 1), scrli);
		cy = cx = -1;
	}

	/*LINTED*/
	curscr->_cury = (short) cy;
	/*LINTED*/
	curscr->_curx = (short) cx;
	return (OK);
}

/* Do the actual insert/delete lines */

static void
_do_idln(int tsy, int bsy, int idn, int doinsert)
{
	int	y, usecsr, yesscrl;
	short	*begns;

	/* change scrolling region */
	yesscrl = usecsr = FALSE;
	if (tsy > 0 || bsy < scrli) {
		if (change_scroll_region) {
		    _PUTS(tparm_p2(change_scroll_region, tsy, bsy - 1),
			bsy - tsy);
		    cy = cx = -1;
		    yesscrl = usecsr = didcsr = TRUE;
		}
	} else {
		if (didcsr) {
		    _PUTS(tparm_p2(change_scroll_region, 0, scrli - 1), scrli);
		    cy = cx = -1;
		    didcsr = FALSE;
		}
		yesscrl = TRUE;
	}

	if (doinsert) {
		/* memory below, clobber it now */
		if (memory_below && clr_eol &&
		    ((usecsr && non_dest_scroll_region) || bsy == scrli)) {
			for (y = bsy - idn, begns = _BEGNS + y;
			    y < bsy; ++y, ++begns)
				if (*begns < scrco) {
					(void) mvcur(cy, cx, y, 0);
					cy = y;
					cx = 0;
					_PUTS(clr_eol, 1);
				}
		}

		/* if not change_scroll_region, delete, then insert */
		if (!usecsr && bsy < scrli) {
			/* delete appropriate number of lines */
			(void) mvcur(cy, cx, bsy - idn, 0);
			cy = bsy - idn;
			cx = 0;
			if (parm_delete_line && (idn > 1 || !delete_line))
				_PUTS(tparm_p1(parm_delete_line, idn),
				    scrli - cy);
			else
				for (y = 0; y < idn; ++y)
			_PUTS(delete_line, scrli - cy);
		}

		/* now do insert */
		(void) mvcur(cy, cx, tsy, 0);
		cy = tsy;
		cx = 0;
		if (yesscrl) {
			if (!parm_rindex && (!scroll_reverse ||
			    (parm_insert_line && idn > 1))) {
				goto hardinsert;
			}
			if (parm_rindex && (idn > 1 || !scroll_reverse))
				_PUTS(tparm_p1(parm_rindex, idn), scrli - cy);
			else
				for (y = 0; y < idn; ++y)
					_PUTS(scroll_reverse, scrli - cy);
		} else {
hardinsert:
			if (parm_insert_line && (idn > 1 || !insert_line))
				_PUTS(tparm_p1(parm_insert_line, idn),
				    scrli - cy);
			else
				for (y = 0; y < idn; ++y)
					_PUTS(insert_line, scrli - cy);
		}
	} else {
		/* doing deletion */
		/* memory above, clobber it now */
		if (memory_above && clr_eol &&
		    ((usecsr && non_dest_scroll_region) || tsy == 0)) {
			for (y = 0, begns = _BEGNS + y + tsy;
			    y < idn; ++y, ++begns)
				if (*begns < scrco) {
					(void) mvcur(cy, cx, tsy + y, 0);
					cy = tsy + y;
					cx = 0;
					_PUTS(clr_eol, 1);
				}
		}

		if (yesscrl) {
			if (!parm_index && (!scroll_forward ||
			    (parm_delete_line && idn > 1))) {
				goto harddelete;
			}
			(void) mvcur(cy, cx, bsy - 1, 0);
			cy = bsy - 1;
			cx = 0;
			if (parm_index && (idn > 1 || !scroll_forward))
				_PUTS(tparm_p1(parm_index, idn), scrli - cy);
			else
				for (y = 0; y < idn; ++y)
					_PUTS(scroll_forward, scrli - cy);
		} else {
harddelete:
			/* do deletion */
			(void) mvcur(cy, cx, tsy, 0);
			cy = tsy;
			cx = 0;
			if (parm_delete_line && (idn > 1 || !delete_line))
				_PUTS(tparm_p1(parm_delete_line, idn),
				    scrli - cy);
			else
				for (y = 0; y < idn; ++y)
				    _PUTS(delete_line, scrli - cy);
		}

		/* if not change_scroll_region, do insert to restore bottom */
		if (!usecsr && bsy < scrli) {
			y = scrli - 1;
			begns = _BEGNS + y;
			for (; y >= bsy; --y, --begns)
				if (*begns < scrco)
					break;
			if (y >= bsy) {
				(void) mvcur(cy, cx, bsy - idn, 0);
				cy = bsy - idn;
				cx = 0;
				if (parm_insert_line &&
				    (idn > 1 || !insert_line))
					_PUTS(tparm_p1(parm_insert_line, idn),
					    scrli - cy);
				else
					for (y = 0; y < idn; ++y)
						_PUTS(insert_line, scrli - cy);
			}
		}
	}
}
