/**************************************************************
 *
 *	CRISP - Custom Reduced Instruction Set Programmers Editor
 *
 *	(C) Paul Fox, 1989
 *
 *    Please See COPYLEFT notice.
 *
 **************************************************************/
# include	"list.h"
# include	"alt.h"

/**********************************************************************/
/*   Codes used for window directions.				      */
/**********************************************************************/
# define	UP		0
# define	RIGHT		1
# define	DOWN		2
# define	LEFT		3
# define	MOVE_WINDOW 	4
# define	MAX_DIR		4	/* Must be last in list.	*/

Head_p hd_position;		/* save/restore position	*/

struct	pos	{
		int	buffer;
		unsigned long line;
		unsigned long col;
		unsigned long top_line;
		};
		
void
create_buffer()
{
	register BUFFER *bp;
	extern	char	*filename();
	char	*raw_name = get_str(1);
	char	*name = strdup(filename(raw_name));
	int	sys;

	acc_assign_int(-1L);

	/***********************************************/
	/*   If  buffer  by same name already exists,  */
	/*   then  create  new  one  with a different  */
	/*   name.				       */
	/***********************************************/
	bp = bfind(name, FALSE);
	if (bp == NULL)
		bp = bfind(name, TRUE);
	else {
		char	*cp = (char *) chk_alloc(strlen(name) + 10);
		int	buf_id = 0;
		while (1) {
			sprintf(cp, "%s [%d]", name, ++buf_id);
			if (bfind(cp, FALSE) == NULL)
				break;
			}
		bp = bfind(cp, TRUE);
		chk_free((void *) name);
		name = cp;
		}
	sys = (short) argv[3].l_int;
	if (sys)
		bp->b_flag |= BF_SYSBUF | BF_NO_UNDO;
	acc_assign_int((long) bp->b_bufnum);

	bp->b_title = name;
	bclear(bp);
	if (argv[2].l_flags != F_NULL) {
		readin(bp, expand_filename(get_str(2)), 
			argv[4].l_flags == F_NULL ? 
				EDIT_NORMAL : (int) argv[4].l_int);
		chk_free((char *) bp->b_fname);
		bp->b_fname = strdup(get_str(2));
		bp->b_line = bp->b_col = 1;
		}
	bp->b_flag |= BFREAD;
}
void
del_buffer()
{
	killbuffer((u_int16) argv[1].l_int);
}
void
set_buffer_flags()
{	int	bchg = curbp->b_flag & BFCHG;

	if (argv[1].l_flags == F_INT) {
		curbp->b_flag &= argv[1].l_int;
		if (bchg && (curbp->b_flag & BFCHG) == 0)
			curbp->b_nummod = 0;
		}
	if (argv[2].l_flags == F_INT)
		curbp->b_flag |= argv[2].l_int;
}
void
inq_modified()
{	BUFFER	*bp;

	if (argv[1].l_flags == F_NULL)
		acc_assign_int((long) curbp->b_nummod);
	else {
		bp = numberb((u_int16) argv[1].l_int);
		if (bp)
			acc_assign_int((long) bp->b_nummod);
		else
			acc_assign_int(0L);
		}
}

/**********************************************************************/
/*   This  macro  is  used  to select the next (or previous) buffer.  */
/*   First  argument  says  whether  to  include system buffers, and  */
/*   second  argument  is non-zero if we want to go backwards in the  */
/*   list.							      */
/**********************************************************************/
void
next_buffer()
{	register BUFFER	*bp, *bp1;
	extern BUFFER	*scrap_bp;
 	int	sysbufs = argv[1].l_int;

	/***********************************************/
	/*   Don't  do  anything  if  we don't have a  */
	/*   buffer.				       */
	/***********************************************/
	if (curbp == NULL)
		return;	
	/***********************************************/
	/*   Handle forwards.			       */
	/***********************************************/
	if (argv[2].l_int == 0) {
		for (bp = curbp->b_bufp; ; bp = bp->b_bufp) {
			if (bp == NULL)
				bp = bheadp;
			if (bp == scrap_bp && !sysbufs)
				continue;
			if (bp == curbp || (bp->b_flag & BF_SYSBUF) == 0 || sysbufs)
				break;
			}
		acc_assign_int((long) bp->b_bufnum);
		return;
		}
	
	/***********************************************/
	/*   Finding  the  previous  buffer is tricky  */
	/*   if  user  doesn't  want  system buffers,  */
	/*   so   we  have  to  have  an  outer  loop  */
	/*   whilst we keep hitting a system buffer.   */
	/***********************************************/
	bp1 = curbp;
	while (1) {
		/***********************************************/
		/*   If  we're  at the start of the list then  */
		/*   wrap around to end of list.	       */
		/***********************************************/
		if (bheadp == bp1) {
			for (bp = bheadp; bp && bp->b_bufp; bp = bp->b_bufp)
				;
			}
		else {
			for (bp = bheadp; bp; bp = bp->b_bufp) {
				if (bp->b_bufp == bp1)
					break;
				}
			}
		bp1 = bp;
		/***********************************************/
		/*   If  we're  back  where  we started, then  */
		/*   just return current buffer ID.	       */
		/***********************************************/
		if (bp1 == curbp)
			break;
		if (sysbufs || (bp1->b_flag & BF_SYSBUF) == 0)
			break;
		}
	acc_assign_int((long) bp1->b_bufnum);
}
void
set_buffer()
{	BUFFER	*bp;

	if ((bp = numberb((u_int16) argv[1].l_int)) == NULL) {
		ewprintf("set_buffer: no such buffer");
		acc_assign_int(-1L);
		return;
		}

	if (curbp)
		acc_assign_int((long) curbp->b_bufnum);
	else
		acc_assign_int((long) 0);
	if (curwp && curwp->w_bufp == curbp && curbp) {
		curbp->b_line  = curwp->w_line;
		curbp->b_col = curwp->w_col;
		curbp->b_top = curwp->w_top_line;
		}
	curbp = bp;
	set_hooked();
}
void
detach_buffer(wp)
WINDOW	*wp;
{	BUFFER *bp = wp->w_bufp;

	if (bp) {
		bp->b_nwnd--;
		wp->w_bufp = NULL;
		bp->b_line  = wp->w_line;
		bp->b_col = wp->w_col;
		bp->b_top = wp->w_top_line;
		}
}
int
get_dir(str)
char	*str;
{
	short	c;
	char	msg[80];
	int	i = (int) argv[1].l_int;

	/***********************************************/
	/*   If  argument  passed to macro, then make  */
	/*   sure its in range.			       */
	/***********************************************/
	if (argv[1].l_flags == F_INT)
		return (i >= 0 && i <= MAX_DIR) ? i : -1;
	
	/***********************************************/
	/*   Otherwise prompt for the direction.       */
	/***********************************************/
	sprintf(msg, "%s (use cursor keys)", str);
	while (1) {
		/***********************************************/
		/*   Allow  user  to  abort the selection (by  */
		/*   typing <Esc>)			       */
		/***********************************************/
		ewprintf(msg);
		c = getkey((long) 0);

		switch (c) {
			case KEY_UP:	return UP;
			case KEY_DOWN:	return DOWN;
			case KEY_LEFT:	return LEFT;
			case KEY_RIGHT:	return RIGHT;
			case F(1):	return MOVE_WINDOW;
			case ESC:	return -1;
			}
		}
}
void
cre_edge()
{
	register int	i = get_dir("Select side for new window");
	WINDOW	*vsplitwind();
	WINDOW	*splitwind();
	WINDOW	*wp;

	acc_assign_int(0L);

	if (i < 0)
		return;
	if (i == LEFT || i == RIGHT)
		wp = vsplitwind();
	else
		wp = splitwind();

	if (wp && (i == RIGHT || i == DOWN))
		curwp = wp;
	acc_assign_int(1L);
	ewputs("", (char *) NULL);
}
void
del_edge()
{	register int i;
	WINDOW	*adj_wp;

	acc_assign_int(1L);
	if (curwp->w_popup)
		return;

	i = get_dir("Select window edge to delete");

	if ((adj_wp = get_edge(i)) == NULL)
		return;

	switch (i) {
	  case UP:
		curwp->w_y = adj_wp->w_y;
		curwp->w_h += adj_wp->w_h + 1;
		break;
	  case DOWN:
		curwp->w_h += adj_wp->w_h + 1;
		break;
	  case LEFT:
		curwp->w_x = adj_wp->w_x;
		curwp->w_w += adj_wp->w_w + 1;
		break;
	  case RIGHT:
		curwp->w_w += adj_wp->w_w + 1;
		break;
	  }

	if (adj_wp->w_bufp->b_nwnd == 1) {
		adj_wp->w_bufp->b_line = adj_wp->w_line;
		adj_wp->w_bufp->b_col = adj_wp->w_col;
		}
	ewputs("", (char *) NULL);
	delete_window(adj_wp);

	update();

	acc_assign_int(0L);
}
WINDOW	*
get_edge(i)
int	i;
{	register WINDOW	*wp;

	if (i < 0)
		return NULL;

	if (curwp->w_popup)
		return curwp;
	if (i == UP || i == DOWN) {
		for (wp = wheadp; wp; wp = wp->w_wndp)
			if (wp->w_w == curwp->w_w &&
			    wp->w_x == curwp->w_x &&
		    ((i == UP && wp->w_y + wp->w_h + 1 == curwp->w_y) ||
		     (i == DOWN && curwp->w_y + curwp->w_h + 1 == wp->w_y)))
				return wp;
		}
	else {
		for (wp = wheadp; wp; wp = wp->w_wndp)
			if (wp->w_h == curwp->w_h &&
			    wp->w_y == curwp->w_y &&
		 ((i == LEFT && wp->w_x + wp->w_w + 1 == curwp->w_x) ||
		  (i == RIGHT && curwp->w_x + curwp->w_w + 1 == wp->w_x)))
			return wp;
		}
	errorf("Edge does not have just two adjoining windows.");
	return NULL;
}
void
move_edge()
{	register int i = 0;
	int	ch;
	WINDOW	*adj_wp = curwp;
	int	num_to_move = argv[2].l_int;
	int	amount_supplied = argv[2].l_flags == F_INT;

	acc_assign_int(0L);
	
	/***********************************************/
	/*   Tiled   windows   change   there  edges,  */
	/*   whereas   popups   allow   the  user  to  */
	/*   increase   or  decrease  the  size,  and  */
	/*   also move the window around the screen.   */
	/***********************************************/
	if (curwp->w_popup == FALSE) {
		i = get_dir("Select an edge to move");
		if (i < 0)
			return;
		if ((adj_wp = get_edge(i)) == NULL)
			return;
		if (!amount_supplied)
			ewprintf("Move to new edge position and press Enter.");
		}
	else if (!amount_supplied)
		ewprintf("Use arrow keys to move window.");

	acc_assign_int(1L);
	while (1) {
		if (amount_supplied) {
			if (num_to_move == 0)
				break;
			if (i == UP || i == DOWN) {
				if (num_to_move > 0) {
					ch = KEY_DOWN;
					num_to_move--;
					}
				else {
					ch = KEY_UP;
					num_to_move++;
					}
				}
			else {
				if (num_to_move > 0) {
					ch = KEY_RIGHT;
					num_to_move--;
					}
				else {
					ch = KEY_LEFT;
					num_to_move++;
					}
				}
			}
		else {
			if ((ch = getkey((long) 0)) == '\n' || ch == '\r')
				break;
			}
		if (curwp->w_popup) {
			switch (ch) {
			  case KEY_WUP:
			  	if (curwp->w_y > 0) {
				  	curwp->w_h++;
				  	curwp->w_y--;
					}
				break;
			  case KEY_UP:
			  	if (curwp->w_y > 0)
				  	curwp->w_y--;
				break;
			  case KEY_WDOWN:
			  	if (curwp->w_y + curwp->w_h < nrow)
					curwp->w_h++;
				break;
			  case KEY_DOWN:
			  	if (curwp->w_y + curwp->w_h < nrow)
				  	curwp->w_y++;
				break;
			  case KEY_WLEFT:
			  	curwp->w_w++;
			  case KEY_LEFT:
			  	if (curwp->w_x > 1)
				  	curwp->w_x--;
				break;
			  case KEY_RIGHT:
			  	/* Keep at least 4 columns of window on screen. */
			  	if (curwp->w_x + 4 < ncol)
				  	curwp->w_x++;
				break;
			  case KEY_WRIGHT:
			  	curwp->w_w++;
				break;
			  }
			}
		else {
			switch (i) {
			  case UP:
				if (ch == KEY_UP && adj_wp->w_h > 1) {
					curwp->w_y--;
					curwp->w_h++;
					adj_wp->w_h--;
					}
				else if (ch == KEY_DOWN && curwp->w_h > 1) {
					adj_wp->w_h++;
					curwp->w_y++;
					curwp->w_h--;
					}
				break;
			  case DOWN:
				if (ch == KEY_DOWN && adj_wp->w_h > 1) {
					curwp->w_h++;
					adj_wp->w_y++;
					adj_wp->w_h--;
					}
				else if (ch == KEY_UP && curwp->w_h > 1) {
					adj_wp->w_h++;
					adj_wp->w_y--;
					curwp->w_h--;
					}
				break;
			  case LEFT:
				if (ch == KEY_LEFT && adj_wp->w_w > 16) {
					adj_wp->w_w--;
					curwp->w_w++;
					curwp->w_x--;
					}
				else if (ch == KEY_RIGHT && curwp->w_w > 16) {
					adj_wp->w_w++;
					curwp->w_w--;
					curwp->w_x++;
					}
				break;
			  case RIGHT:
				if (ch == KEY_RIGHT && adj_wp->w_w > 16) {
					adj_wp->w_w--;
					adj_wp->w_x++;
					curwp->w_w++;
					}
				else if (ch == KEY_LEFT && curwp->w_w > 16) {
					adj_wp->w_w++;
					adj_wp->w_x--;
					curwp->w_w--;
					}
				break;
			  }
			}
		  
		/***********************************************/
		/*   If  we're  moving  a  popup window, then  */
		/*   do  the  hard  work of working out whats  */
		/*   changed  on  the  screen.  If  its not a  */
		/*   popup  then  just  mark  the two windows  */
		/*   involved  as  needing  some work done to  */
		/*   them.				       */
		/***********************************************/
		sort_windows();
		if (curwp->w_popup)
			harden_windows();
		else {
			set_corners();
			adj_wp->w_flag |= WFHARD;
			win_modify(WFHARD);
			}
		update();
		}
	
	ewprintf("");
}
void
change_window()
{	register int	i;
	char	*cp = get_str(2);
	WINDOW	*wp;

	acc_assign_int(0L);
	if (cp == NULL || *cp == NULL)
		cp = "Point to destination";
	if ((i = get_dir(cp)) < 0) {
		ewprintf("");
		return;
		}
	if (wp = get_window(i)) {
		change_window2(wp);
		ewprintf("");
		}
}
void
change_window2(wp)
WINDOW	*wp;
{
	set_buffer_parms(curwp, curbp);
	curwp->w_flag |= WFHARD;
	wp->w_flag |= WFHARD;
	curwp = wp;
	curbp = curwp->w_bufp;
	set_hooked();
}
WINDOW	*
get_window(i)
int	i;
{
	WINDOW	*wp;
	WINDOW	*new_wp = NULL;
	u_int16	col;
	int	line;

	switch (i) {
	  case UP:
		col = *cur_col + curwp->w_x - curwp->w_indent;
		for (wp = wheadp; wp; wp = wp->w_wndp) {
			if (wp == curwp)
				continue;
			if (wp->w_x <= col && wp->w_x + wp->w_w >= col &&
			    wp->w_y < curwp->w_y) {
				if (new_wp == NULL)
					new_wp = wp;
				else if (new_wp->w_y < wp->w_y)
					new_wp = wp;
				}
			}
		break;
	  case DOWN:
		col = *cur_col + curwp->w_x - curwp->w_indent;
		for (wp = wheadp; wp; wp = wp->w_wndp) {
			if (wp == curwp)
				continue;
			if (wp->w_x <= col && wp->w_x + wp->w_w >= col &&
			    wp->w_y > curwp->w_y) {
				if (new_wp == NULL)
					new_wp = wp;
				else if (new_wp->w_y > wp->w_y)
					new_wp = wp;
				}
			}
		break;
	  case LEFT:
		line = curwp->w_y + (curwp->w_line - curwp->w_top_line) + 1;
		for (wp = wheadp; wp; wp = wp->w_wndp) {
			if (wp == curwp)
				continue;
			if (wp->w_y <= line && wp->w_y + wp->w_h >= line &&
			    wp->w_x < curwp->w_x) {
				if (new_wp == NULL)
					new_wp = wp;
				else if (new_wp->w_x < wp->w_x)
					new_wp = wp;
				}
			}
		break;
	  case RIGHT:
		line = curwp->w_y + (curwp->w_line - curwp->w_top_line) + 1;
		for (wp = wheadp; wp; wp = wp->w_wndp) {
			if (wp == curwp)
				continue;
			if (wp->w_y <= line && wp->w_y + wp->w_h >= line &&
			    wp->w_x > curwp->w_x) {
				if (new_wp == NULL)
					new_wp = wp;
				else if (new_wp->w_x > wp->w_x)
					new_wp = wp;
				}
			}
		break;
	  case MOVE_WINDOW:
	  	new_wp = curwp->w_wndp ? curwp->w_wndp : wheadp;
		break;
	  }
	if (new_wp) {
		acc_assign_int(1L);
		return new_wp;
		}
	errorf("No window there.");
	acc_assign_int(0L);
	return 0;
}
void
save_position()
{	BUFFER	*bp;
	struct pos *pos = (struct pos *) chk_alloc(sizeof (struct pos));

	if (hd_position == NULL)
		hd_position = ll_init();
	pos->buffer = curbp ? curbp->b_bufnum : 0;
	pos->line = *cur_line;
	pos->col = *cur_col;
	bp = curwp ? curwp->w_bufp : NULL;
	pos->top_line = (curwp && bp == curbp) ? curwp->w_top_line : -1;
	ll_push(hd_position, (char *) pos);
}
void
restore_position()
{
	struct pos *pos = NULL;
	List_p lp;
	BUFFER	*saved_bp = curbp;

	if (hd_position == NULL)
		hd_position = ll_init();

	lp = ll_first(hd_position);
	if (lp == NULL) {
		acc_assign_int(0L);
		errorf("restore_position: no saved position.");
		return;
		}
	pos = (struct pos *) ll_elem(lp);
	ll_pop(hd_position);

	if ((argv[1].l_flags == F_NULL || argv[1].l_int) && pos) {
		argv[1].l_flags = argv[2].l_flags = F_INT;
		argv[1].l_int = pos->line;
		argv[2].l_int = pos->col;
		if (curbp = numberb(pos->buffer)) {
			set_hooked();
			move_abs();
/*			if (curwp && curwp->w_bufp == curbp)
				curwp->w_top_line = pos->top_line;*/
			curbp = saved_bp;
			set_hooked();
			}
		}
	acc_assign_int(1L);
}
void
inq_views()
{	BUFFER *bp;

	acc_assign_int(0L);
	if (argv[1].l_flags == F_INT) {
		bp = numberb((u_int16) argv[1].l_int);
		if (bp)
			acc_assign_int((long) bp->b_nwnd);
		}
	else
		acc_assign_int((long) curbp->b_nwnd);
}
void
inq_lines()
{	BUFFER	*bp = argv[1].l_flags == F_INT ? 
		numberb((u_int16) argv[1].l_int) : curbp;

	acc_assign_int((long) (bp ? (bp->b_numlines - 1) : -1));
}
static int
sort_compare(line1, line2)
LINE	*line1, *line2;
{	int	len = line1->l_used;
	int	ret;
	register unsigned char *cp1 = ltext(line1);
	register unsigned char *cp2 = ltext(line2);

	if (line2->l_used < len)
		len = line2->l_used;
	while (len-- > 0) {
		ret = *cp1++ - *cp2++;
		if (ret)
			return ret;
		}

	return line1->l_used - line2->l_used;
}
void
sort_buffer()
{	int	num_lines;
	extern int	start_line, end_line;
	LINE	*lp, *clp;
	register int i;

	if (get_marked_areas((WINDOW *) NULL) == FALSE) {
		start_line = 1;
		end_line--;
		}
	num_lines = end_line - start_line + 1;
	lp = (LINE *) chk_alloc(num_lines * sizeof (LINE));
	for (i = 0; i < num_lines; i++) {
		clp = vm_lock_line(start_line + i);
		lp[i] = *clp;
		}
	qsort(lp, num_lines, sizeof (LINE), sort_compare);
	for (i = 0; i < num_lines; i++) {
		clp = vm_lock_line(start_line + i);
		clp->l_size = lp[i].l_size;
		clp->l_used = lp[i].l_used;
		clp->l_lineno = lp[i].l_lineno;
		clp->l_flags = lp[i].l_flags;
		clp->l_text = lp[i].l_text;
		}
	win_modify(WFHARD);
}
