/**************************************************************
 *
 *	CRISP - Custom Reduced Instruction Set Programmers Editor
 *
 *	(C) Paul Fox, 1989, 1990, 1991
 *
 *    Please See COPYRIGHT notice.
 *
 **************************************************************/
/*------------------------------------------------------
 *    File containing various mapping functions.
 *------------------------------------------------------*/
# include	"list.h"

/**********************************************************************/
/*   The  following  variable  is  a  bit  mask  controlling certain  */
/*   attributes  of  the  display, such as shadowing. See list.h for  */
/*   bit definitions, and description of display_mode() macro.	      */
/**********************************************************************/
int	display_ctrl = DC_SHADOW;

int	virtual_space = FALSE;
LINE	*global_lp;

/**********************************************************************/
/*   Line  to  play with when we want a line when cursor past end of  */
/*   buffer.							      */
/**********************************************************************/
LINE	static_line;

/**********************************************************************/
/*   Fast  macro  to  tell  the  width  of a character. If its not a  */
/*   single  width  character  then  call char_width1 to do the real  */
/*   work.							      */
/**********************************************************************/
# define char_width(pos, offset, cpp) \
		(cur_cmap->cm_flags[_x_ = **cpp] ? \
		char_width1(pos, offset, cpp) : \
		((*cpp)++, (*offset)--, cur_cmap->cm_length[_x_]))
/**********************************************************************/
/*   Function  called  to  work out how wide a character is when its  */
/*   printed  or  for  converting  column  positions  to offsets and  */
/*   vice versa.						      */
/**********************************************************************/
int
char_width1(pos, offset, cpp)
int	pos;
register unsigned char **cpp;
int	*offset;
{
	register int	off;
	register unsigned char *cp;
	int	ch = **cpp;
	int	fl = cur_cmap->cm_flags[ch];
			
	if (fl == CMAP_TAB) {
		(*cpp)++;
		(*offset)--;
		return next_tab_stop(pos) + 1 - pos;
		}
	/***********************************************/
	/*   If  we're  not  in  ansi mode, then just  */
	/*   make the character appear normally'.      */
	/***********************************************/
	if ((curbp->b_flag & BFANSI) == 0) {
		(*cpp)++;
		(*offset)--;
		return cur_cmap->cm_length[ch];
		}
	/***********************************************/
	/*   Backspaces  effectively  move  us back a  */
	/*   column.				       */
	/***********************************************/
	if (ch == '\b') {
		(*cpp)++;
		if (*offset > 1) {
			(*offset)--;
			return -1;
			}
		return 0;
		}
	off = *offset;
	for (cp = *cpp; off-- >= 0 && !isalpha(*cp); cp++)
		;
	if (off < 0 || *cp != 'm') {
		(*cpp)++;
		(*offset)--;
		return cur_cmap->cm_length[0x1b];
		}
	*offset = off;
	*cpp = cp+1;
	return 0;
}

void
display_mode()
{
	acc_assign_int((long) display_ctrl);
	harden_windows();
}


/************************************************************************
 *                                                                      *
 *				linep                                   *
 *				-----                                   *
 *                                                                      *
 *  Purpose:                                                            *
 *                                                                      *
 *     Take a line number and return a pointer to the line header.      *
 *     Does optimisations to avoid travelling around the ring.          *
 ************************************************************************/

LINE	*
linep(line)
register int line;
{	register int diff;
	register LINE *lp;
	int cline;

	if (curbp == NULL)
		return NULL;
	/***********************************************/
	/*   If  we're  past  the  end  of the buffer  */
	/*   then  return  a line to play with but it  */
	/*   shouldn't be modified.		       */
	/***********************************************/
	if (line > curbp->b_numlines) {
		return &static_line;
		}
	if ((cline = curbp->b_cline) == line)
		return curbp->b_clinep;
	if (cline > line) {
		diff = cline - line;
		if (diff < line) {
			lp = curbp->b_clinep;
go_back:
			while (diff-- > 0)
				lp = lback(lp);
			curbp->b_cline = line;
			curbp->b_clinep = lp;
			return lp;
			}
		diff = line - 1;
		lp = lforw(curbp->b_linep);
		}
	else {
		diff = curbp->b_numlines - line;
		if (diff < line - cline) {
			lp = curbp->b_linep;
			goto go_back;
			}
		diff = line - cline;
		lp = curbp->b_clinep;
		}
	while (diff-- > 0)
		lp = lforw(lp);
	curbp->b_clinep = lp;
	curbp->b_cline = line;
	return lp;
}
void
flush_cache(bp)
BUFFER	*bp;
{
	bp->b_clinep = lforw(bp->b_linep);
	bp->b_cline = 1;
}
/************************************************************************
 *                                                                      *
 *				current_col                             *
 *				-----------                             *
 *                                                                      *
 *  Purpose:                                                            *
 *                                                                      *
 *     Convert the current column position into an absolute position    *
 *     offset to the current line.                                      *
 *----------------------------------------------------------------------*
 *  Input Parameters:                                                   *
 *----------------------------------------------------------------------*
 *  Output:                                                             *
 *----------------------------------------------------------------------*
 *  Global variables modified:                                          *
 ************************************************************************/
int
current_col(offset)
int	offset;
{	register int	col = 1;
	LINE	*lp;
	register int _x_;
	unsigned char	*cp;
	
	lp = vm_lock_line(*cur_line);
	cp = ltext(lp);
	while (offset > 0) {
		col += char_width(col, &offset, &cp);
		}
	vm_unlock(*cur_line);
	return col;
}

/************************************************************************
 *                                                                      *
 *				current_offset                          *
 *				--------------                          *
 *                                                                      *
 *  Purpose:                                                            *
 *                                                                      *
 *     Convert current column position into an offset from the start    *
 *     of the line. If the fill is set and cursor is not on a valid     *
 *     character, fill the line with tabs/spaces so that it becomes     *
 *     valid.                                                           *
 *----------------------------------------------------------------------*
 *  Input Parameters:                                                   *
 *----------------------------------------------------------------------*
 *  Output:                                                             *
 *----------------------------------------------------------------------*
 *  Global variables modified:                                          *
 ************************************************************************/
int
current_offset(col, fill)
register int col;
int	fill;
{	register int	pos;
	register int _x_;
	int	last_pos;
	int	i;
	int	width;
	u_char *cp;
	int len;
	int	esc_char;
	
	width = 0;
	pos = 1;
	global_lp = vm_lock_line(*cur_line);
	cp = ltext(global_lp);
	i = len = llength(global_lp);
	
	/***********************************************/
	/*   In  non-ansi  mode, we're not interested  */
	/*   in  ESC's.  In  ansi mode we always want  */
	/*   the   cursor  to  be  AFTER  the  unseen  */
	/*   escape sequences.			       */
	/***********************************************/
	esc_char = curbp->b_flag & BFANSI ? 0x1b : 0x100;
	while (pos <= col && len > 0) {
		if (pos == col && *cp != esc_char)
			break;
		width = char_width(pos, &len, &cp);
		pos += width;
		}
	/***********************************************/
	/*   If  we've  gone  too far and there stuff  */
	/*   to  our  right,  then  we  need  to skip  */
	/*   back one character.		       */
	/***********************************************/
	if (pos > col && len) {
		cp--;
		len++;
		pos -= width;
		}
	/***********************************************/
	/*   We  got  a  virtual  space  if we're not  */
	/*   sitting   on   the   first   part  of  a  */
	/*   multi-byte  character  and the character  */
	/*   that    started   this   has   the   TAB  */
	/*   attribute set.			       */
	/***********************************************/
	virtual_space = pos != col;
	if (!virtual_space || fill == FALSE) {
		i = cp - ltext(global_lp);
		vm_unlock(*cur_line);
		if (fill == FALSE && pos > col)
			return i - 1;
		return i;
		}
	i -= len;
	/*-------------------------------------
	 *   The following condition is true if cursor
	 *   is past the end of the line. We can then 
	 *   tab and space fill.
	 *-------------------------------------*/
	if (pos < col)
		i = space_fill(global_lp, col - pos, i, pos);
	else {
		/*-------------------------------------
	 	*   The following condition is true if cursor
	 	*   is NOT past the end of the line. In
	 	*   this case we can only space fill.
	 	*-------------------------------------*/
		if (len) {
			last_pos = pos - width;
			i = space_fill(global_lp, col - last_pos, i - 1, last_pos);
			}
		else
			i = space_fill(global_lp, pos - col, i-1, col);
		}
	vm_unlock(*cur_line);
	return i;
}
int
tab_replace()
{	int offset = current_offset(*cur_col, FALSE);
	LINE	*lp = vm_lock_line(*cur_line);
	int	result = FALSE;
	if (lp->l_flags & L_FILE)
		lnormal(lp, (u_int32) 0);
		
	/***********************************************/
	/*   If  we  are  on last line of buffer then  */
	/*   l_text not yet allocated, so set it up.   */
	/***********************************************/
	if (lp->l_text == NULL) {
		lp->l_size = offset+16;
		lp->l_used = offset;
		lp->l_text = (u_char *) chk_alloc(offset+16);
		memset(lp->l_text, ' ', offset);
		}
	if (offset < llength(lp) && lp->l_text[offset] == '\t') {
		int	col = *cur_col;
		int	ntab = next_tab_stop(*cur_col);
		current_offset(ntab - 1, TRUE);
		*cur_col = ntab - 1;
		ldelete((RSIZE) 1);
		l_insert(' ');
		*cur_col = col;
		result = TRUE;
		}
	vm_unlock(*cur_line);
	return result;
}
int
next_tab_stop(col)
int	col;
{
	register u_int16 *tp = curbp->b_tabs;
	u_int16	tabstop;
	int	c;

	while (*tp && *tp < col)
		tp++;
	if (*tp) 
		return *tp;

	if (tp > &curbp->b_tabs[1])
		c = tp[-1], tabstop = tp[-1] - tp[-2];
	else if (tp == &curbp->b_tabs[1])
		c = tp[-1], tabstop = tp[-1];
	else
		c = 0, tabstop = 8;
	while (c < col)
		c += tabstop;
	return c;
}
