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

/**********************************************************************/
/*   Allocate memory for keystroke macro in 32 key chunk.	      */
/**********************************************************************/
# define	STORAGE_INCR	32

struct playback {
	int	id;		/* ID of the keystroke macro.		*/
	int	buffer_id; 	/* Buffer containing disassembled version */
			  	/* of keys.			*/
	ref_t	*ref;		/* Buffer containing keystrokes. */
	int	out_index;	/* Index for next character to retrieve. */
	};
	
static Head_p	hd_kmacs;

struct playback *get_kmac PROTO((int, int));
static	struct playback *ptr_info = NULL;
int	last_rem_index = 0;
int	defining_macro = FALSE;
int	playing_back = FALSE;
int	rem_nest_level = -1;
int	rem_doing_self_insert;
char	*rem_string = "  ";

void
init_playback()
{
	hd_kmacs = ll_init();
}
void
do_pause()
{
	if (defining_macro)
		rem_string = *rem_string == 'P' ? "RE" : "PA";
	else if (playing_back)
		rem_string = *rem_string == 'P' ? "  " : "PA";
	else
		return;
	line_col(TRUE);
}
void
remember()
{
	char ch = argv[1].l_flags == F_NULL ? 0 : get_str(1)[0];

	acc_assign_int((long) -1L);	
	if (playing_back)
		return;
		
	if (!defining_macro) {
		if (ptr_info != NULL) {
			if (ch != 'y' && ch != 'Y' &&
			    !eyorn("Overwrite existing keystroke macro"))
				return;
				}
		if ((ptr_info = get_kmac(argv[2].l_flags == F_NULL ? 
				-1 : (int) argv[2].l_int, TRUE)) == NULL)
			return;
		}
	
	if (ptr_info->buffer_id == 0) {
		char	buf[20];
		BUFFER	*bp;
		sprintf(buf, "KBD-MACRO-%d", ptr_info->id);
		bp = bfind(filename(buf), TRUE);
		bp->b_flag |= BFREAD | BF_NO_UNDO | BF_SYSBUF;
		ptr_info->buffer_id = bp->b_bufnum;
		}

	if (!defining_macro) {
 		if (ptr_info->ref == NULL) {
			ptr_info->ref = r_init(F_STR, "", 1);
			if (ch == 'n' || ch == 'N')
				return;
			ewprintf("Defining keystroke macro.");
			rem_string = "RE";
			}
		defining_macro = TRUE;
		bclear(numberb(ptr_info->buffer_id));
		rem_nest_level = -1;
		rem_doing_self_insert = FALSE;
		ptr_info->ref->r_used = 0;
		ptr_info->out_index = 0;
		}
	else {
		defining_macro = FALSE;
		ewprintf("Keystroke macro defined.");
		rem_string = "  ";
		ptr_info->out_index = 0;
		acc_assign_int(ptr_info->id);
		}

	line_col(TRUE);
}
/**********************************************************************/
/*   Return  pointer  to  a  playback  structure.  create_flag  says  */
/*   whether  we  should  create  entry  if  not  already there. The  */
/*   first arg  is  playback  macro index. If not specified then use  */
/*   the last macro (or next one).				      */
/**********************************************************************/
struct playback *
get_kmac(id, create_flag)
int	id;
int	create_flag;
{	register struct playback *pp = NULL;
	register List_p	lp;

	if (id < 0) {
		if (create_flag)
			id = ++last_rem_index;
		else
			id = last_rem_index;
		}
		
	for (lp = ll_first(hd_kmacs); lp; lp = ll_next(lp)) {
		pp = (struct playback *) ll_elem(lp);
		if (pp->id == id)
			break;
		}
	if (lp)
		return pp;
	if (!create_flag)
		return NULL;
	pp = (struct playback *) chk_alloc(sizeof(struct playback));
	pp->id = id;
	pp->buffer_id = 0;
	pp->ref = NULL;
	pp->out_index = 0;
	ll_append(hd_kmacs, (char *) pp);
	return pp;
}
void
playback()
{
	ptr_info = get_kmac(argv[1].l_flags == F_NULL ? 
		-1 : (int) argv[1].l_int, FALSE);
	if (playing_back || ptr_info == NULL)
		return;
	if (defining_macro) {
		infof("Can't play back while remembering.");
		acc_assign_int(-1L);
		return;
		}
	infof("Playing back keystroke macro.");
	playing_back = TRUE;
	ptr_info->out_index = 0;
	acc_assign_int(0L);
}
/**********************************************************************/
/*   Function  called  when  a macro is executed as a result of user  */
/*   hitting  a  key.  Used to store the disassembled version of the  */
/*   keystroke macro.						      */
/**********************************************************************/
void
remember_macro(cp)
char *cp;
{
	BUFFER *saved_bp = curbp;
	BUFFER	*bp;
		
	if (!defining_macro || rem_string[0] == 'P')
		return;
	if (rem_nest_level == -1)
		rem_nest_level = nest_level;
	else if (rem_nest_level != nest_level)
		return;
		
	/***********************************************/
	/*   Find  the  buffer  associated  with this  */
	/*   keyboard   macro.   If  the  buffer  has  */
	/*   disappeared,  i.e.  user  has deleted it  */
	/*   then  don't  bother  trying  to save the  */
	/*   disassembled keystroke info.	       */
	/***********************************************/
	if ((bp = numberb(ptr_info->buffer_id)) == NULL)
		return;
	curbp = bp;
	set_hooked();
	curbp->b_flag |= BF_NO_UNDO;
	if (*cp == 's' && strcmp(cp, "self_insert") == 0) {
		char buf[5];
		char *bp = buf;
		extern char character;
		if (rem_doing_self_insert) {
			switch (character) {
			  case '\\':
			  case '"':
				*bp++ = '\\';
				break;
			  }
			*bp++ = character;
			llinsert(buf, (u_int32) (bp - buf), FALSE);
			}
		else {
			llinsert("insert(", (u_int32) 7, FALSE);
			*bp++ = '"';
			switch (character) {
			  case '\\':
			  case '"':
				*bp++ = '\\';
				break;
			  }
			*bp++ = character;
			llinsert(buf, (u_int32) (bp - buf), FALSE);
			rem_doing_self_insert = TRUE;
			}
		}
	else {
		if (rem_doing_self_insert)
			llinsert("\");", (u_int32) 3, TRUE);
		if (*cp == 'r' && strcmp(cp, "remember") == 0) {
			char *msg = "/* End of macro */";
			llinsert(msg, (u_int32) strlen(msg), TRUE);
			}
		else {
			/***********************************************/
			/*   Need  to  put commas and brackets around  */
			/*   arguments.				       */
			/***********************************************/
			int	done_open = FALSE;
			while (*cp == ' ')
				cp++;
			while (*cp) {
				llinsert(cp, (u_int32) 1, FALSE);
				cp++;
				if (*cp == ' ') {
					if (done_open)
						llinsert(", ", (u_int32) 2, FALSE);
					else
						llinsert("(", (u_int32) 1, FALSE);
					done_open = TRUE;
					cp++;
					continue;
					}
				}
			if (!done_open)
				llinsert("();", (u_int32) 3, TRUE);
			else
				llinsert(");", (u_int32) 2, TRUE);
			}
		rem_doing_self_insert = FALSE;
		}
		
	curbp = saved_bp;
	set_hooked();
}
void
store_char(ch)
int	ch;
{	unsigned short	us = (unsigned short) ch;

	if (defining_macro && rem_string[0] != 'P') {
		r_append(ptr_info->ref, (char *) &us, sizeof us, STORAGE_INCR); 
		}
}
int
grab_char(get_it)
int	get_it;
{	unsigned short ch;

	if (playing_back) {
		if (*rem_string == 'P')
			return 0;
		if (ptr_info->out_index >= 
		    (ptr_info->ref->r_used / sizeof (unsigned short)) - 1) {
			playing_back = FALSE;
			u_chain();
			infof("Playback successful.");
			return 0;
			}
		ch = ((unsigned short *) ptr_info->ref->r_ptr)[ptr_info->out_index];
		if (get_it)
			ptr_info->out_index++;
		return ch;
		}
	return 0;
}
/**********************************************************************/
/*   Return   a   string  containing  the  characters  making  up  a  */
/*   keyboard macro which can be saved in a file and reloaded.	      */
/**********************************************************************/
void
inq_keystroke_macro()
{	char	*kp;
	struct playback *pp;
	register unsigned short *usp;
	register unsigned short *end_usp;
	ref_t	*rp;
	
	if (argv[1].l_flags == F_NULL)
		pp = ptr_info;
	else
		pp = get_kmac((int) argv[1].l_int, FALSE);
	if (pp == NULL) {
		acc_assign_str("", 1);
		return;
		}
	argv_assign(2, (long) pp->buffer_id);
	
	rp = r_init(F_STR, "", 1);
	usp = (unsigned short *) pp->ref->r_ptr;
	end_usp = (unsigned short *) &pp->ref->r_ptr[pp->ref->r_used];
	end_usp--;
	while (usp < end_usp) {
	     	kp = code_to_keyname((int) *usp++);
		r_cat(rp, kp);
	     	}
	acc_assign_ref(rp);
}

/****************************************************************************/
/*   Macro to create a new keystroke macro.				    */
/****************************************************************************/
void
load_keystroke_macro()
{
	struct playback *pp;
	unsigned short *keys;
	int	len;
	char	*cp = get_str(1);
	/***********************************************/
	/*   Create a new keystroke buffer.	       */
	/***********************************************/
	pp = get_kmac(-1, TRUE);
	/***********************************************/
	/*   Convert key strokes to internal codes.    */
	/***********************************************/
	keys = cvt_string_to_code(cp, &len);
	pp->ref = r_init(F_STR, (char *) keys, len);
	acc_assign_int(pp->id);
}
