/* $Id$ */

#include <alloc.h>
#include <stdio.h>
#include <assert.h>

#include "position.h"
#include "tree.h"
#include "operator.h"
#include "misc.h"

extern FILE	*db_out;
extern int	db_ss;

typedef struct item {
  struct item	*i_next;
  struct tree	*i_node;
  int		i_disabled;
  int		i_itemno;
} t_item, *p_item;

/* STATICALLOCDEF "item" 10 */

struct itemlist {
  p_item	il_first, il_last;
};

static struct itemlist	item_list;
static int	stop_reason;
int		item_count;

static int
in_item_list(p)
  p_tree	p;
{
  register p_item i = item_list.il_first;

  while (i) {
	if (i->i_node == p) return 1;
	i = i->i_next;
  }
  return 0;
}

static
pr_item(i)
  p_item	i;
{
  fprintf(db_out, "(%d)\t", i->i_itemno);
  print_node(db_out, i->i_node, 0);
  fputs(i->i_disabled ? " (disabled)\n": "\n", db_out);
}

int
item_addr_actions(a, mess_type, may_stop)
  t_addr	a;
  int		mess_type;
  int		may_stop;
{
  /* Perform actions associated with position 'a', and return stop_reason
     if we must stop there, and 0 if not.
  */
  register p_item i = item_list.il_first;

  stop_reason = 0;
  for (i = item_list.il_first; i != 0; i = i->i_next) {
	register p_tree	p = i->i_node;

	if (! i->i_disabled
	    && (p->t_address == a || p->t_address == NO_ADDR)) {
		switch(p->t_oper) {
		case OP_STOP:
			if (mess_type != 1) break;
			if (! p->t_args[1] || eval_cond(p->t_args[1])) {
				if (! stop_reason) stop_reason = i->i_itemno;
			}
			break;
		case OP_TRACE:
		case OP_WHEN:
		case OP_DUMP:
		case OP_DISPLAY:
			break;
		default:
			assert(0);
		}
	}
  }
  for (i = item_list.il_first; i != 0; i = i->i_next) {
	register p_tree	p = i->i_node;

	if (! i->i_disabled
	    && (p->t_address == a || p->t_address == NO_ADDR)) {
		switch(p->t_oper) {
		case OP_TRACE:
			if ((! stop_reason && mess_type != 0)
			    || p->t_args[2] || ! may_stop) {
				perform(p, a);
			}
			break;
		case OP_WHEN:
			perform(p, a);
			break;
		case OP_STOP:
		case OP_DUMP:
		case OP_DISPLAY:
			break;
		default:
			assert(0);
		}
	}
  }
  return stop_reason;
}

handle_displays()
{
  register p_item i = item_list.il_first;

  while (i) {
	register p_tree p = i->i_node;

	if (! i->i_disabled && p->t_oper == OP_DISPLAY) do_print(p);
	i = i->i_next;
  }
}

add_to_item_list(p)
  p_tree	p;
{
  p_item i;
  
  if (in_item_list(p)) return;

  i = new_item();
  i->i_node = p;
  if (p->t_address == NO_ADDR
      && (p->t_oper != OP_TRACE || ! p->t_args[0])) db_ss++;
  if (item_list.il_first == 0) {
	item_list.il_first = i;
  }
  else {
	item_list.il_last->i_next = i;
  }
  i->i_itemno = ++item_count;
  item_list.il_last = i;
  pr_item(i);
}

remove_from_item_list(n)
  int	n;
{
  register p_item i = item_list.il_first, prev = 0;
  p_tree	p;

  if (n == 0) {
	n = stop_reason;
	if (n == 0) {
		error("no current stopping point");
		return;
	}
	stop_reason = 0;
  }
  if (n < 0) n = item_count + n + 1;
  while (i) {
	if (i->i_itemno == n) break;
	prev = i;
	i = i->i_next;
  }
  if (! i) {
	error("no item %d in current status", n);
	return;
  }
  if (i->i_itemno == stop_reason) stop_reason = 0;
  if (prev) {
	prev->i_next = i->i_next;
  }
  else item_list.il_first = i->i_next;
  if (i == item_list.il_last) item_list.il_last = prev;
  p = i->i_node;
  if (p->t_address == NO_ADDR
      && (p->t_oper != OP_TRACE || ! p->t_args[0])) db_ss--;
  free_item(i);
  switch(p->t_oper) {
  case OP_STOP:
  case OP_WHEN:
	(void) setstop(p, 0);
	break;
  case OP_TRACE:
	(void) settrace(p, 0);
	break;
  case OP_DUMP:
	free_dump(p);
	break;
  }
  freenode(p);
}

p_tree
get_from_item_list(n)
  int	n;
{
  register p_item i = item_list.il_first;

  if (n == 0) {
	n = stop_reason;
	if (n == 0) return 0;
  }
  if (n < 0) n = item_count + n + 1;
  while (i) {
	if (i->i_itemno == n) return i->i_node;
	i = i->i_next;
  }
  return 0;
}

able_item(n, kind)
  int	n;
{
  register p_item i = item_list.il_first;
  register p_tree p;

  if (n == 0) {
	n = stop_reason;
	if (n == 0) {
		error("no current stopping point");
		return;
	}
  }
  if (n < 0) n = item_count + n + 1;
  while (i) {
	if (i->i_itemno == n) break;
	i = i->i_next;
  }
  if (! i) {
	error("no item %d in current status", n);
	return;
  }
  p = i->i_node;
  if (i->i_disabled == kind) {
	warning("item %d already %sabled", n, kind ? "dis" : "en");
	return;
  }
  if (p->t_address == NO_ADDR
      && (p->t_oper != OP_TRACE || ! p->t_args[0])) {
	db_ss += kind == 1 ? (-1) : 1;
  }
  i->i_disabled = kind;
  switch(p->t_oper) {
  case OP_STOP:
  case OP_WHEN:
	(void) setstop(p, ! kind);
	break;
  case OP_TRACE:
	(void) settrace(p, ! kind);
	break;
  }
}

print_items()
{
  register p_item i = item_list.il_first;

  for (; i; i = i->i_next) {
	pr_item(i);
  }
}

perform_items()
{
  register p_item i = item_list.il_first;

  for (; i; i = i->i_next) {
	if (! i->i_disabled && i->i_node->t_oper != OP_DUMP) eval(i->i_node);
  }
}