/* Cque.c */

#include <ctype.h>
#include <fcntl.h>
#include <string.h>
#ifdef XENIX
#include <sys/signal.h>
#else
#include <signal.h>
#endif				/* xenix */

#include "config.h"
#include "db.h"
#include "interface.h"
#include "match.h"
#include "externs.h"

#ifdef MEM_CHECK
#include "mem_check.h"
#endif

#ifdef USE_NALLOC
#include "nalloc.h"
extern NALLOC *glurp;
NALLOC *big = NULL;
#endif

extern char ccom[];
extern dbref cplr;

extern int atoi();

typedef struct bque BQUE;

struct bque {
  BQUE *next;
  dbref player;			/* player who will do command */
  dbref cause;			/* player causing command (for %N) */
  int left;			/* seconds left until execution */
  char *env[10];		/* environment, from wild match */
  char *comm;			/* command to be executed */
};

static BQUE *qfirst = NULL, *qlast = NULL, *qwait = NULL;
static BQUE *qlfirst = NULL, *qllast = NULL;

void parse_que(player, command, cause)
    dbref player;
    char *command;
    dbref cause;  /* cause is needed to determine priority */
{
  char buff[BUFFER_LEN], *s, *r;
  void big_que();
  s = buff;
  strcpy(buff, command);
  while (r = parse_up(&s, ';')) {
    big_que(player, r, cause);
  }
}

static int add_to(player, am)
    dbref player;
    int am;
{
  int num = 0;
  ATTR *a;
  char buff[MAX_COMMAND_LEN];

  player = db[player].owner;
  a = atr_get(player, "QUEUE");
  if(a)
    num = atoi(a->value);
  num += am;
  if (num > 0)
    sprintf(buff, "%d", num);
  else
    *buff = 0;
  (void) atr_add(player, "QUEUE", buff, GOD, NOTHING);
  return (num);
}

void big_que(player, command, cause)
    dbref player;
    char *command;
    dbref cause;
{
  int a;
  BQUE *tmp;

#ifdef USE_NALLOC
  if(!big)
    big = na_open(sizeof(char));
#endif
  if ((Typeof(player) != TYPE_PLAYER) && (db[player].flags & HALT))
    return;
  /* make sure player can afford to do it */
  if (!payfor(player, QUEUE_COST + (((random() & QUEUE_LOSS) == 0) ? 1 : 0))) {
    notify(db[player].owner, "Not enough money to queue command.");
    return;
  }
  if (add_to(player, 1) > QUEUE_QUOTA) {
    notify(db[player].owner, "Run away objects, commands halted");
    do_halt(db[player].owner, "");
    /* halt means no command execution allowed */
    db[player].flags |= HALT;
    return;
  }
#ifdef USE_NALLOC
  tmp = (BQUE *) na_alloc(big, sizeof(BQUE));
  strcpy((tmp->comm = (char *) na_ualloc(big, strlen(command)+1)), command);
#else
  tmp = (BQUE *) malloc (sizeof(BQUE));
  strcpy((tmp->comm = (char *) malloc(strlen(command)+1)), command);
#endif
#ifdef MEM_CHECK
  add_check("bqueue");
  add_check("bqueue_comm");
#endif
  tmp->player = player;
  tmp->next = NULL;
  tmp->left = 0;
  tmp->cause = cause;
  for (a = 0; a < 10; a++)
    if (!wptr[a])
      tmp->env[a] = NULL;
    else {
#ifdef USE_NALLOC
      strcpy((tmp->env[a] =(char *)na_ualloc(big, strlen(wptr[a])+1)),wptr[a]);
#else
      strcpy((tmp->env[a] = (char *)malloc(strlen(wptr[a])+1)), wptr[a]);
#endif
#ifdef MEM_CHECK
      add_check("bqueue_env");
#endif
    }
  if (Typeof(cause) == TYPE_PLAYER) {
    if (qlast) {
      qlast->next = tmp;
      qlast = tmp;
    } else
      qlast = qfirst = tmp;
  } else {
    if (qllast) {
      qllast->next = tmp;
      qllast = tmp;
    } else
      qllast = qlfirst = tmp;
  }
}

void wait_que(player, wait, command, cause)
    dbref player;
    int wait;
    char *command;
    dbref cause;
{
  BQUE *tmp;
  int a;
  /* make sure player can afford to do it */
  if (!payfor(player, QUEUE_COST + (((random() & QUEUE_LOSS) == 0) ? 1 : 0))) {
    notify(player, "Not enough money to queue command.");
    return;
  }
#ifdef USE_NALLOC
  tmp = (BQUE *) na_alloc(big, sizeof(BQUE));
  strcpy((tmp->comm = (char *)na_ualloc(big, strlen(command)+1)), command);
#else
  tmp = (BQUE *)malloc(sizeof(BQUE));
  strcpy((tmp->comm = (char *)malloc(strlen(command)+1)), command);
#endif
#ifdef MEM_CHECK
  add_check("bqueue");
  add_check("bqueue_comm");
#endif
  tmp->player = player;
  tmp->next = qwait;
  tmp->left = wait;
  tmp->cause = cause;
  for (a = 0; a < 10; a++)
    if (!wptr[a])
      tmp->env[a] = NULL;
    else {
#ifdef USE_NALLOC
      strcpy((tmp->env[a] = (char *)na_ualloc(big,strlen(wptr[a])+1)),wptr[a]);
#else
      strcpy((tmp->env[a] = (char *)malloc(strlen(wptr[a])+1)), wptr[a]);
#endif
#ifdef MEM_CHECK
      add_check("bqueue_env");
#endif
    }
  qwait = tmp;
}

/* call every second to check for wait queue commands */
void do_second()
{
  BQUE *trail = NULL, *point, *next;
  /* move contents of low priority queue onto end of normal one */
  /* this helps to keep objects from getting out of control since */
  /* its affects on other objects happen only after one seconds */
  /* this should allow @halt to be type before getting blown away */
  /* by scrolling text */
  if (qlfirst) {
    if (qlast)
      qlast->next = qlfirst;
    else
      qfirst = qlfirst;
    qlast = qllast;
    qllast = qlfirst = NULL;
  }
  for (point = qwait; point; point = next)
    /*
     * Note: this would be 0 except the command is being put in the low
     * priority queue to be done in one second anyways
     */
    if (point->left-- <= 1) {
      int a;
      giveto(point->player, QUEUE_COST);
      for (a = 0; a < 10; a++) {
	wptr[a] = point->env[a];
      }
      parse_que(point->player, point->comm, point->cause);
      if (trail)
	trail->next = next = point->next;
      else
	qwait = next = point->next;
      for (a = 0; a < 10; a++)
	if (point->env[a]) {
#ifdef USE_NALLOC
	  na_unalloc(big, point->env[a]);
#else
	  free((char *)point->env[a]);
#endif
#ifdef MEM_CHECK
	  del_check("bqueue_env");
#endif
	}
#ifdef USE_NALLOC
      if (point->comm)
	      na_unalloc(big, point->comm);
      na_unalloc(big, point);
#else
      if (point->comm)
	      free((char *)point->comm);
      free((char *)point);
#endif
#ifdef MEM_CHECK
      del_check("bqueue_comm");
      del_check("bqueue");
#endif
    } else
      next = (trail = point)->next;
}

int test_top()
{
  return (qfirst ? 1 : 0);
}

/* execute one command off the top of the queue */
int do_top()
{
  int a;
  int i = 0;
  BQUE *tmp;
  dbref player;
  char tbuf1[BUFFER_LEN], tbuf2[BUFFER_LEN];
  char tbuf3[BUFFER_LEN], *s;

  if (!qfirst)
    return (0);
  if (qfirst->player && !(db[qfirst->player].flags & GOING)) {
    giveto(qfirst->player, QUEUE_COST);
    cplr = qfirst->player;
    strcpy(ccom, qfirst->comm);
    add_to(player = qfirst->player, -1);
    qfirst->player = 0;
    if ((Typeof(player) == TYPE_PLAYER) || !(db[player].flags & HALT)) {
      for (a = 0; a < 10; a++) {
	wptr[a] = qfirst->env[a];
      }
      strcpy(tbuf2, uncompress(qfirst->comm));
      tbuf3[0] = '\0';
      for (s = tbuf2; *s; tbuf3[i++] = toupper(*s++)) ;
      tbuf3[i++] = '\0';
      if ((strstr(tbuf3, "@DOL")) == NULL)
        strcpy(tbuf1, pronoun_substitute(qfirst->cause, tbuf2, player));
      else
        strcpy(tbuf1, tbuf2);
      if (IS(player, TYPE_THING, THING_VERBOSE))
	notify(db[player].owner, tprintf("#%d] %s", player, tbuf1));
      process_command(player, tbuf1, qfirst->cause);
    }
  }
  tmp = qfirst->next;
  for (a = 0; a < 10; a++)
    if (qfirst->env[a]) {
#ifdef USE_NALLOC
      na_unalloc(big, qfirst->env[a]);
#else
      free((char *) qfirst->env[a]);
#endif
#ifdef MEM_CHECK
      del_check("bqueue_env");
#endif
    }
#ifdef USE_NALLOC
  if (qfirst->comm)
	  na_unalloc(big, qfirst->comm);
  na_unalloc(big, qfirst);
#else
  if (qfirst->comm)
	  free((char *)qfirst->comm);
  free((char *)qfirst);
#endif
#ifdef MEM_CHECK
  del_check("bqueue_comm");
  del_check("bqueue");
#endif
  if (!(qfirst = tmp))
    qlast = NULL;
  return (1);
}

/* tell player what commands they have pending in the queue */
void do_queue(player, what)
    dbref player;
    const char *what;
{
  BQUE *tmp;
  dbref victim = NOTHING;
  int everything = 0;
  int quick = 0;
  int pq = 0, oq = 0, wq = 0;
  int tpq = 0, toq = 0, twq = 0;

  if (!string_compare(what, "count")) {
    victim = player;
    everything = 1;
    quick = 1;
  }
  else if(Wizard(player)) {
    if(!what || !*what)
      victim = player;
    else
      if(!string_compare(what, "all")) { /* do the whole thing */
        victim = player;
        everything = 1;
      } else {
        init_match(player, what, TYPE_PLAYER);
        match_player();
        match_absolute();
        match_me();
        victim = match_result();
      }
  } else {
    victim = player;
  }

  switch(victim) {
    case NOTHING:
      notify(player, "I couldn't find that player.");
      break;
    case AMBIGUOUS:
      notify(player, "I don't know who you mean!");
      break;
    default:
      if(everything == 1)
        notify(player, "Queue for : all");
      else
        notify(player, tprintf("Queue for : %s", db[victim].name));

      if (!quick) notify(player,"Player Queue:");
      for (tmp = qfirst; tmp; tmp = tmp->next) {
	tpq++;
        if ((db[tmp->player].owner == db[victim].owner) || (everything)) {
	  pq++;
	  if (!quick)
          notify(player, tprintf("%s:%s", unparse_object(player, tmp->player),
				 tmp->comm));
	}
      }
      if (!quick) notify(player,"Object Queue:");
      for (tmp = qlfirst; tmp; tmp = tmp->next) {
	toq++;
        if ((db[tmp->player].owner == db[victim].owner) || (everything)) {
	  oq++;
	  if (!quick)
          notify(player, tprintf("%s:%s", unparse_object(player, tmp->player),
				 tmp->comm));
	}
      }
      if (!quick) notify(player,"Wait Queue:");
      for (tmp = qwait; tmp; tmp = tmp->next) {
	twq++;
        if ((db[tmp->player].owner == db[victim].owner) || (everything)) {
	  wq++;
	  if (!quick)
          notify(player, tprintf("[%d]%s:%s", tmp->left,
				 unparse_object(player, tmp->player),
				 tmp->comm));
	}
      }
      notify(player,"------------  Queue Done  ------------");
      notify(player, 
	     tprintf("Totals: Player...%d/%d   Object...%d/%d   Wait...%d/%d",
		     pq, tpq, oq, toq, wq, twq));
  }
}

/* remove all queued commands from a certain player */
/* it cannot be called on specific objects */

void do_halt(player, ncom)
    dbref player;
    char *ncom;
{
  BQUE *tmp, *trail = NULL, *point, *next;
  int num = 0;
  notify(db[player].owner, "Halted.");
  for (tmp = qfirst; tmp; tmp = tmp->next)
    if ((tmp->player == player) || (db[tmp->player].owner == player)) {
      num--;
      giveto(player, QUEUE_COST);
      tmp->player = 0;
    }
  for (tmp = qlfirst; tmp; tmp = tmp->next)
    if ((tmp->player == player) || (db[tmp->player].owner == player)) {
      num--;
      giveto(player, QUEUE_COST);
      tmp->player = 0;
    }
  /* remove wait q stuff */
  for (point = qwait; point; point = next)
    if ((point->player == player) || (db[point->player].owner == player)) {
      int a;

      giveto(player, QUEUE_COST);
      if (trail)
        trail->next = next = point->next;
      else
        qwait = next = point->next;
      for (a = 0; a < 10; a++)
        if (point->env[a]) {
#ifdef USE_NALLOC
	  na_unalloc(big, point->env[a]);
#else
	  free((char *)point->env[a]);
#endif
#ifdef MEM_CHECK
	  del_check("bqueue_env");
#endif
	}
#ifdef USE_NALLOC
      if (point->comm)
	      na_unalloc(big, point->comm);
      na_unalloc(big, point);
#else
      if (point->comm)
	      free((char *) point->comm);
      free((char *) point);
#endif
#ifdef MEM_CHECK
      del_check("bqueue_comm");
      del_check("bqueue");
#endif
    } else
      next = (trail = point)->next;

  if (db[player].owner == player)
    (void) atr_add(player, "QUEUE", "", GOD, NOTHING);
  else
    add_to(player, num);
  if (*ncom)
    parse_que(player, ncom, player);
}

void do_halt1(player, arg1, arg2)
  dbref player;
  const char *arg1;
  const char *arg2;
{
  dbref victim;
  char temp[BUFFER_LEN];
  if (*arg1 == '\0')
    do_halt(player, "");
  else {
    init_match(player, arg1, NOTYPE);
    match_neighbor();
    match_possession();
    match_me();
    match_absolute();
    match_player();
    if ((victim = noisy_match_result()) == NOTHING)
      return;
    if ((db[victim].owner != player) && (!Wizard(player))) {
      notify(player, "Permission denied.");
      return;
    }
    if (Typeof(victim) != TYPE_PLAYER) {
      sprintf(temp, "#%d", victim);
      do_set(player, temp, "HALT");
    } else {
      if (victim != player)
	notify(player, "Halted.");
      do_halt(victim, arg2);
    }
  }
}

void do_allhalt(player)
    dbref player;
{
  dbref victim;
  if(!Wizard(player)) {
    notify(player, "Sorry, you can only halt your own items.");
    return;
  }
  for(victim = 0;victim <db_top; victim++) {
    if(Typeof(victim) == TYPE_PLAYER) {
      notify(victim,tprintf("Your objects have been globally halted by %s",
			    db[player].name));
      do_halt(victim,"");
    }
  }
}