/**********************************************************************/
/*                                                                    */
/*	CRISP - Programmable editor                                   */
/*	===========================                                   */
/*                                                                    */
/*  File:          kbdmap.c                                           */
/*  Author:        P. D. Fox                                          */
/*  Created:       14 May 1991                     		      */
/*                                                                    */
/*  Copyright (c) 1990, 1991 Paul Fox                                 */
/*                All Rights Reserved.                                */
/*                                                                    */
/*                                                                    */
/*--------------------------------------------------------------------*/
/*  Description:  Primitives to handle screen and keyboard mapping    */
/*                                                                    */
/**********************************************************************/

# include	"list.h"
# include	<ctype.h>
# include	"alt.h"

Head_p	hd_cmap;	/* List of character maps defined so far.	*/
cmap_t	*base_cmap;
cmap_t	*default_cmap;	/* The default char map used for windows and buffers */
			/* when non explicitly defined.			*/
cmap_t	*cur_cmap;	/* The current char map for the current buffer/window */

int	cmap_id = 0;
PHYS_TERM	pt;


struct pt_map {
	int	isbool;
	char	*ptr;
	};
	
struct pt_map pt_chars[] = {
	FALSE,		pt.pt_top_left,
	FALSE,		pt.pt_top_right,
	FALSE,		pt.pt_bot_left,
	FALSE,		pt.pt_bot_right,
	FALSE,		pt.pt_vertical,
	FALSE,		pt.pt_horizontal,
	FALSE,		pt.pt_top_join,
	FALSE,		pt.pt_bot_join,
	FALSE,		pt.pt_cross,
	FALSE,		pt.pt_left_join,
	FALSE,		pt.pt_right_join,
	NULL,		NULL
	};
struct pt_map pt_features[] = {
	FALSE,		pt.pt_space,
	FALSE,		pt.pt_character,
	FALSE,		pt.pt_icursor,
	FALSE,		pt.pt_ocursor,
	FALSE,		pt.pt_vicursor,
	FALSE,		pt.pt_vocursor,
	FALSE,		pt.pt_escape,
	FALSE,		pt.pt_repeat,
	TRUE,		&pt.pt_0m,
	TRUE,		&pt.pt_color,
	FALSE,		pt.pt_escC,
	TRUE,		pt.pt_clr_color_is_black,
	TRUE,		pt.pt_noscroll,
	FALSE,		pt.pt_graphics,
	FALSE,		pt.pt_text,
	NULL,		NULL
	};
	
/**********************************************************************/
/*   Prototypes.						      */
/**********************************************************************/
void	set_display_chars PROTO((void));
void	st_characters PROTO((void));
void	st_features PROTO((void));
void	st_common PROTO((struct pt_map *));
void	st_keyboard PROTO((void));
static void	st_key PROTO((int, LISTV *));
void	gt_characters PROTO((void));
void	gt_common PROTO((struct pt_map *));
void	gt_keyboard PROTO((void));
void	gt_features PROTO((void));
cmap_t	*find_cmap PROTO((int));

/**********************************************************************/
/*   Function  to  initialise the default cmap. Ctrl characters plus  */
/*   DEL  are  printed  as  ^x.  Characters with the top bit set get  */
/*   printed   depending   on   whether  the  terminal  can  support  */
/*   printing  the  8-bit  character  directly or not. If it cannot,  */
/*   we print these characters in hex notation.			      */
/**********************************************************************/
void
init_cmap()
{	register int i;
	int	map_size;
	char	*cp;
	int	printable_top_bit;
	extern char hex_digits[];
	
	printable_top_bit = pt.pt_character[0] != NULL;

	/***********************************************/
	/*   If  called  the first time, make sure we  */
	/*   allocate some memory.		       */
	/***********************************************/
	if (hd_cmap == NULL) {
		hd_cmap = ll_init();

		base_cmap = (cmap_t *) chk_alloc(sizeof (cmap_t));
		default_cmap = base_cmap;
		base_cmap->cmap_id = cmap_id++;
		memset((char *) base_cmap, 0, sizeof base_cmap);
		/***********************************************/
		/*   Allocate   maximum   memory  we're  ever  */
		/*   likely to use.			       */
		/***********************************************/
		map_size = (32 * 3) + (95 * 2) + 3 + (5 * 128);
		cp = chk_alloc(map_size);
		i = 0;
		ll_append(hd_cmap, (char *) base_cmap);
		}
	else {
		/***********************************************/
		/*   We  only  need  to  change functionailty  */
		/*   of top bits.			       */
		/***********************************************/
		cp = base_cmap->cm_str[0x80];
		i = 0x80;
		}

	for (; i < MAX_CMAP_CHARS; i++) {
		base_cmap->cm_str[i] = cp;
		if (i < ' ' || i == 0x7f) {
			base_cmap->cm_length[i] = 2;
			*cp++ = '^';
			*cp++ = i == 0x7f ? '?' : (i + '@');
			}
		else if (i < 0x80 || printable_top_bit) {
			base_cmap->cm_length[i] = 1;
			*cp++ = i;
			}
		else {
			base_cmap->cm_length[i] = 4;
			*cp++ = '\\';
			*cp++ = 'x';
			*cp++ = hex_digits[(i >> 4) & 0xf];
			*cp++ = hex_digits[i & 0xf];
			}
		*cp++ = NULL;
		}
	/***********************************************/
	/*   We  grab  some  of the top chars for the  */
	/*   line drawing graphics.		       */
	/***********************************************/
	for (i = CH_MIN; i <= CH_MAX; i++) {
		base_cmap->cm_length[i] = 1;
		cp = base_cmap->cm_str[i];
		*cp++ = i;
		*cp = NULL;
		}
		
	/***********************************************/
	/*   Tab is always handled special.	       */
	/***********************************************/
	base_cmap->cm_flags['\t'] = CMAP_TAB;
	/***********************************************/
	/*   Escapes  may  need  processing if buffer  */
	/*   is in ANSI mode.			       */
	/***********************************************/
	base_cmap->cm_flags['[' & 0x1f] = CMAP_ESCAPE;
	/***********************************************/
	/*   Backspaces  need  special  processing in  */
	/*   ANSI mode.				       */
	/***********************************************/
	base_cmap->cm_flags['h' & 0x1f] = CMAP_BACKSPACE;
	
	/***********************************************/
	/*   End of line is not marked special.	       */
	/***********************************************/
	base_cmap->cm_eol = " ";	
}
void
st_characters()
{
	st_common(pt_chars);
}
void
st_features()
{
	st_common(pt_features);
	
	/***********************************************/
	/*   Re-initialise  the  top  128  characters  */
	/*   in the display map for the base_cmap.     */
	/***********************************************/
	init_cmap();
}
void
st_common(p)
register struct pt_map *p;
{	LISTV	result;
	register LIST	*lp = argv[1].l_list;
	int	type;

	for ( ; lp && *lp != F_HALT && p->ptr; p++, lp = next_atom(lp)) {
		char	*cp = "";
		type = eval(lp, &result);
		if (p->isbool) {
			*p->ptr = (char) result.l_int;
			continue;
			}
		switch (type) {
		  case F_INT:
			p->ptr[0] = (char) result.l_int;
			p->ptr[1] = NULL;
			continue;
		  case F_NULL:
		  	continue;
		  case F_STR:
		  case F_LIT:
			cp = result.l_str;
			break;
		  case F_RSTR:
 			cp = result.l_ref->r_ptr;
			break;
		  default:
		  	continue;
		  }
		tcopy_string(p->ptr, cp, NULL);
		}

}
void
st_keyboard()
{	LISTV	result;
	int	key_no;
	LIST	*lp = argv[1].l_list;
	LIST	*lp1;
	char	buf[2];
	OPCODE	type;

	for (; lp && *lp != F_HALT; lp = next_atom(lp)) {
		type = eval(lp, &result);
		if (type != F_INT)
			break;
		key_no = result.l_int;
		lp = next_atom(lp);
		type = eval(lp, &result);
		switch (type) {
		  case F_LIST:
			lp1 = result.l_list;
			break;
		  case F_RLIST:
		  	lp1 = (LIST *) result.l_ref->r_ptr;
			break;
	 	  default:
			st_key(key_no, &result);
			continue;
		  }
		while (lp1 && *lp1 != F_HALT) {
			switch (*lp1) {
			  case F_INT:
			  	buf[0] = (char) LGET32(lp1);
				buf[1] = NULL;
				key_define_key_seq(key_no, buf);
				break;
			  case F_STR: case F_LIT:
			  	key_define_key_seq(key_no, (char *) LGET32(lp1));
				break;
			  case F_RSTR:
			  	key_define_key_seq(key_no, 
					((ref_t *) LGET32(lp1))->r_ptr);
				break;
			  }
			lp1 = next_atom(lp1);
			key_no++;
			}
		}
}
static void
st_key(key_no, result)
int	key_no;
LISTV	*result;
{
	char	buf[2];
	
	switch (result->l_flags) {
	  case F_INT:
	  	buf[0] = (char) result->l_int;
		buf[1] = NULL;
		key_define_key_seq(key_no, buf);
		break;
	  case F_STR: case F_LIT:
	  	key_define_key_seq(key_no, result->l_str);
		break;
	  case F_RSTR:
	  	key_define_key_seq(key_no, result->l_ref->r_ptr);
		break;
	  default:
	  	panic("default");
	  }
}
void
gt_characters()
{
	gt_common(pt_chars);
}
void
gt_common(pt)
register struct	pt_map	*pt;
{	register int	i;
	for (i = 0; pt[i].ptr; i++)
		if (argv[i+1].l_flags != F_NULL) {
			if (pt[i].isbool)
				int_assign(argv[i+1].l_sym, (long) *pt_chars[i].ptr);
			else
				str_assign(argv[i+1].l_sym, pt_chars[i].ptr);
			}
}
void
gt_keyboard()
{
}
void
gt_features()
{
	gt_common(pt_features);
}
/**********************************************************************/
/*   Primitive  to  create  a  character-map  for  mapping  the  way  */
/*   characters appear on output.				      */
/**********************************************************************/
void
create_char_map()
{
	register cmap_t	*cmp;
	char	*cp;
	register int	i = argv[2].l_int;
	int	j;
	register LIST	*lp = get_list(3);
	
	/***********************************************/
	/*   Look  up  the  specified  cmap.  If  not  */
	/*   specified, create a new one.	       */
	/***********************************************/
	if (argv[1].l_flags == F_NULL) {
		cmp = (cmap_t *) chk_alloc(sizeof *cmp);
		memset((char *) cmp, 0, sizeof *cmp);
		cmp->cmap_id = cmap_id++;
		cmp->cm_eol = " ";
		}
	else {
		cmp = find_cmap((int) argv[1].l_int);
		if (cmp == NULL) {
			acc_assign_int((long) -1L);
			return;
			}
		}
	/***********************************************/
	/*   Process the display character list.       */
	/***********************************************/
	for ( ; i < MAX_CMAP_CHARS && lp && *lp != F_HALT; i++, lp = next_atom(lp)) {
		cp = get_str_ptr(lp);
		if (cp == NULL)
			continue;
		cmp->cm_length[i] = strlen(cp);
		cmp->cm_str[i] = strdup(cp);
		}

	/***********************************************/
	/*   If  we've  still  got  an atom left, and  */
	/*   we  are  after  the 256 ASCII characters  */
	/*   then   use   the   257th  entry  as  the  */
	/*   end-of-line marker.		       */
	/***********************************************/
	if (i == MAX_CMAP_CHARS && lp && *lp != F_HALT) {
		cp = get_str_ptr(lp);
		if (cp)
			cmp->cm_eol = strdup(cp);
		}		
	/***********************************************/
	/*   Fill  in  all  character  positions  not  */
	/*   currently defined.			       */
	/***********************************************/
	for (i = 0; i < MAX_CMAP_CHARS; i++) {
		if (cmp->cm_length[i] == 0) {
			cmp->cm_length[i] = base_cmap->cm_length[i];
			cmp->cm_str[i] = strdup(base_cmap->cm_str[i]);
			cmp->cm_flags[i] = base_cmap->cm_flags[i];
			}
		}
	/***********************************************/
	/*   Now process the flags list.	       */
	/***********************************************/
	lp = get_list(4);
	if (lp) {
		/***********************************************/
		/*   Flags  are  a  pair  of  numbers  -- the  */
		/*   first  is  the  character  position, and  */
		/*   the 2nd is the flag value.		       */
		/***********************************************/
		for ( ; *lp != F_HALT; lp = next_atom(lp)) {
			if (*lp != F_INT)
				continue;
			i = (int) LGET32(lp);
			lp = next_atom(lp);
			if (*lp == F_INT)
				j = (int) LGET32(lp);
			else
				j = 0;
			if (i < 0 || i >= MAX_CMAP_CHARS)
				continue;
			cmp->cm_flags[i] = j;
			}
		}
	acc_assign_int((long) cmp->cmap_id);
	ll_push(hd_cmap, (char *) cmp);
}
/**********************************************************************/
/*   Function  to  attach a character map to a window so that we can  */
/*   see the data in a different way.				      */
/**********************************************************************/
void
set_window_cmap()
{	WINDOW	*wp;
	cmap_t	*cmp = default_cmap;
	
	/***********************************************/
	/*   First  find  the cmap specified. If none  */
	/*   specified  then  we  want  to remove the  */
	/*   reference to the cmap.		       */
	/***********************************************/
	if (argv[1].l_flags != F_NULL) {
		cmp = find_cmap((int) argv[1].l_int);
		if (cmp == NULL)
			return;
		}	   
	/***********************************************/
	/*   If  no  window specified then it must be  */
	/*   the global one we're setting.	       */
	/***********************************************/
	if (argv[2].l_flags == F_NULL) {
		default_cmap = cmp;
		return;
		}

	/***********************************************/
	/*   Find window to modify.		       */
	/***********************************************/
	wp = find_window((int) argv[2].l_int);
	if (wp == NULL)
		return;
	wp->w_cmap = cmp;
	/***********************************************/
	/*   Mark the window as being dirty.	       */
	/***********************************************/
	wwin_modify(wp, WFHARD);
}
/**********************************************************************/
/*   Attach  a  char  map  to a buffer. This has precedence over the  */
/*   windows view of a buffer.					      */
/**********************************************************************/
void
set_buffer_cmap()
{	BUFFER	*bp;
	cmap_t	*cmp = NULL;

	acc_assign_int((long) -1L);	
	if (argv[1].l_flags != F_NULL) {
		cmp = find_cmap((int) argv[1].l_int);
		if (cmp == NULL)
			return;
		}	   
	/***********************************************/
	/*   If   no   buffer   specified   then  use  */
	/*   current buffer.			       */
	/***********************************************/
	if (argv[2].l_flags == F_NULL) {
		bp = curbp;
		}
	else
		bp = numberb(argv[2].l_int);
	if (bp == NULL)
		return;
	bp->b_cmap = cmp;
	acc_assign_int((long) cmp->cmap_id);
	harden_windows();
}
/**********************************************************************/
/*   Function to find a character map given the id.		      */
/**********************************************************************/
cmap_t *
find_cmap(id)
int	id;
{	register List_p	lp;
	register cmap_t *cmp;
	
	for (lp = ll_first(hd_cmap); lp; lp = ll_next(lp)) {
		cmp = (cmap_t *) ll_elem(lp);
		if (cmp->cmap_id == id)
			return cmp;
		}
	return NULL;
}

/**********************************************************************/
/*   Return current character map for window specified.		      */
/**********************************************************************/
void
inq_char_map()
{	WINDOW	*wp;

	if (argv[1].l_flags == F_NULL)
		wp = curwp;
	else
		wp = find_window((int) argv[1].l_int);
	if (wp)
		acc_assign_int(wp->w_bufp && wp->w_bufp->b_cmap ?
			wp->w_bufp->b_cmap->cmap_id : wp->w_cmap->cmap_id);
	else
		acc_assign_int((long) curbp && curbp->b_cmap ? 
				curbp->b_cmap->cmap_id : -1L);
}
