/*************************************************************
/*   CRISP debugging package.
/*************************************************************/

# include	"crisp.h"
# include	"debug.h"

# define	DEBUG	0	/* Set to 1 if we are debugging debugger*/
int	_dbg_old_line = -1;	/* Old value of line number - stops us */
				/* from keep tracing the same line. */
int	_dbg_win = -1;
int	_dbg_buf;
int	_dbg_kbd;		/* Keyboard map for debugger.		*/
list	_dbg_bpt;		/* List of breakpoints.			*/
int	_dbg_nbrk = 0;		/* Number of breakpoints active.	*/
int	_dbg_on = FALSE;	/* If TRUE, (debug) has been called.	*/
									  
list	__dbg_type_info = {
	"?0?    ", 
	"int    ",
	"string ",
	"list   ",
	"null   ",
	"id     ",
	"end    ",
	"lit    ",
	"rstr   ",
	"float  ",
	"list   "
	};
void
__dbg_init()
{	extern int window_offset;

	window_offset += 40;
	_dbg_win = sized_window(20, 48, "<Alt-H> for help.");
	window_offset -= 40;
}

void
trace()
{	string	macro_name;
	int	_dbg_saved_win;	/* If _dbg_win == -1, then _dbg_saved_win */
				/* is real value of _dbg_win so we can  */
				/* delete window when we finish tracing */
				/* macro.				*/

	get_parm(0, macro_name, "Macro to trace : ");
	if (macro_name == "")
		return;
	__dbg_init();
	execute_macro(macro_name);
	if (_dbg_win == -1)
		_dbg_win = _dbg_saved_win;
	set_window(_dbg_win);
	delete_window();
	_dbg_win = -1;
	message("Macro %s completed.", macro_name);
}
/*************************************************************
/*   This function is called by the debug info put into
/*   the .m/.cm file produced by 'crunch -g'.
/*************************************************************/
void
__dbg_trace__()
{
	/*----------------------------------------
	/*   Turn off debugging before we hit the
	/*   declarations. Makes log file easier to
	/*   read.
	/*----------------------------------------*/
	if (_dbg_on)
		debug(DEBUG);

	{int	curbuf,
		curwin,
		msg_printed,
		line_no;
	string filename, macro_name;
	extern int _dbg_saved_win;

	get_parm(0, line_no);
	get_parm(2, macro_name);
	msg_printed = FALSE;
	if (_dbg_win == -1 || _dbg_old_line == line_no) {
		/*----------------------------------------
		/*   See if we have a breakpoint.
		/*----------------------------------------*/
		if (_dbg_nbrk == 0 || _dbg_old_line == line_no 
		   || re_search(NULL, "<" + macro_name + ">", _dbg_bpt) < 0) {
		   	if (_dbg_on)
				debug(1);
			return;
			}
		if (_dbg_win == -1)
			_dbg_win = _dbg_saved_win;
		msg_printed = TRUE;
		message("Breakpoint in %s", macro_name);
		}
	curbuf = inq_buffer();
	curwin = inq_window();
	
	_dbg_old_line = line_no;
	set_window(_dbg_win);
	get_parm(1, filename);
	edit_file(filename);
	select_buffer(inq_buffer(), _dbg_win, SEL_NORMAL, 
		__dbg_keylist(), NULL, NULL, line_no, 1);
	if (msg_printed)
		message("");
	set_buffer(curbuf);
	set_window(curwin);
	attach_buffer(curbuf);
   	if (_dbg_on)
		debug(1);
	}
}
void
__dbg_keylist()
{
	assign_to_key("s", "exit");
	assign_to_key("b", "buffer_list 1");
	assign_to_key("c", "__dbg_c");
	assign_to_key("d", "__dbg_d");
	assign_to_key("k", "_dbg_stack \"__dbg_trace__\"");
}
void
__dbg_c()
{	extern int _dbg_saved_win;

	_dbg_saved_win = _dbg_win;
	_dbg_win = -1;
	exit();
}
void
__dbg_d()
{
	_dbg_on = !_dbg_on;
	if (_dbg_on)
		message("Debug tracing turned on.");
	else
		message("Debug tracing turned off.");
}
void
_dbg_stack(string start_fn)
{	
	list stack = debug_support(DBG_STACK_TRACE, NULL, start_fn);

	select_slim_list("Stack", "", stack, 0,
		NULL, "_dbg_stack_keys");
}
/**********************************************************************/
/*   Function called to set up key assignments for the stack trace.   */
/**********************************************************************/
void
_dbg_stack_keys()
{
	assign_to_key("<Enter>", "_dbg_s_enter");
	assign_to_key("<Alt-G>", "vars");
}
/**********************************************************************/
/*   Function  to  look  at  variables  defined at the current stack  */
/*   level.							      */
/**********************************************************************/
void
_dbg_s_enter()
{	extern list stack;
	int	line;

	inq_position(line);
	vars(length_of_list(stack) - line);
}
/***********************************************************************
/*   Macro to set a breakpoint at a certain line/file or macro.
/***********************************************************************/
void
brk(string macro_name)
{
	if (macro_name == "") {
		if (get_parm(NULL, macro_name, "Macro name: ") <= 0 ||
		   (macro_name = trim(macro_name)) == "") {
			if (_dbg_nbrk == 0) {
				message("No breakpoints active.");
				return;
				}
			select_list("Breakpoint List", "", 1, _dbg_bpt, 0);
			return;
			}
		}
	_dbg_bpt[_dbg_nbrk] = macro_name;
	++_dbg_nbrk;
}

/**********************************************************************/
/*   Macro to evaluate argument and print result.		      */
/**********************************************************************/
void
eval()
{
	declare	arg;
	int	ret, p;

	if (get_parm(0, arg, "Argument: ") <= 0)
		return;
	p = pause_on_error();
	ret = execute_macro(arg);
	pause_on_error(p);
	message("Result=%d", ret);
}

/**********************************************************************/
/*   Temporary macro to print the current execution nesting level.    */
/**********************************************************************/
void
inq_nest_level()
{
	int lev = debug_support(DBG_NEST_LEVEL);
	message("Nesting level=%d", lev);
}
/**********************************************************************/
/*   Display all global variables.				      */
/**********************************************************************/
void
vars()
{
	list	lst, lst1;
	int	len, i;
	int	buf, curbuf;
	int	win, curwin;
	string	var, name;
	declare	v;
	int	level;
		
	curbuf = inq_buffer();
	curwin = inq_window();
	
	/***********************************************/
	/*   If  we  get passed a stack level to look  */
	/*   at,   use  it,  otherwise  look  at  the  */
	/*   global symbols.			       */
	/***********************************************/
	if (get_parm(0, level) <= 0)
		level = -1;	
	lst = debug_support(DBG_INQ_VARS, level);
	if ((len = length_of_list(lst)) == 0) {
		message("No local variables defined.");
		return;
		}
	message("");

	/***********************************************/
	/*   If  we  have  a stack level then use the  */
	/*   name  of  that  function  as  the window  */
	/*   title.				       */
	/***********************************************/
	if (level >= 0) {
		lst1 = debug_support(DBG_STACK_TRACE, NULL, "");
		name = lst1[length_of_list(lst1) - level - 1] + " #" + level;
		}
	else
		name = "Global Variables";
	buf = create_buffer(name, NULL, TRUE);
	set_buffer(buf);
	for (i = 0; i < len; ) {
		var = lst[i++];
		lst1 = debug_support(DBG_INQ_VAR_INFO, level, var);
		insert(__dbg_type_info[lst1[0]]);
		insert(var + " = ");
		v = lst1[1];
		switch (typeof(v)) {
		  case "string":
		  	v = gsub("\"", "\\\\\"", v);
		  	insert("\"" + v + "\"");
			break;
		  case "list":
		  	if (length_of_list(v) == 0)
				insert("NULL");
			else
			  	insert("{list}");
			break;
		  case "NULL":
		  	insert("NULL");
			break;
		  default:
		  	insert("" + v);
		  }
		insert("\n");
		}
	win = sized_window(inq_lines(), inq_line_length());
	select_buffer(buf, win);

	delete_buffer(buf);
	set_buffer(curbuf);
	set_window(curwin);
	attach_buffer(curbuf);
}
