/* boolexp.c */
#include "copyright.h"

#include <ctype.h>
#include <string.h>

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

int eval_boolexp(player, b, privs, nrecurs, locktype)
    dbref player;
    struct boolexp *b;
    dbref privs;
    int nrecurs;
    int locktype;
{
  ATTR *a;
  char tbuf1[BUFFER_LEN], tbuf2[BUFFER_LEN];

  if(player < 0 || player >= db_top)
    return 0;

  if (nrecurs > MAX_DEPTH) {
    notify(player,"Sorry, broken lock!");
    return 0;
  }
  if (b == TRUE_BOOLEXP) {
    return 1;
  } else {
    switch (b->type) {
      case BOOLEXP_IND:
	if(b->sub1->type != BOOLEXP_CONST)
	  return 0;
    	nrecurs++;
        switch (locktype) {
	case BASICLOCK:
	  return (eval_boolexp(player, db[b->sub1->thing].key, privs, nrecurs,
			       locktype));
	  break;
	case USELOCK:
	  return (eval_boolexp(player, db[b->sub1->thing].usekey, privs,
			       nrecurs, locktype));
	  break;
	case ENTERLOCK:
	  return (eval_boolexp(player, db[b->sub1->thing].enterkey, privs,
			       nrecurs, locktype));
	  break;
	default:
	  /* should never be reached */
	  notify(player, "Broken lock!");
	  break;
	} break;
      case BOOLEXP_AND:
	return (eval_boolexp(player, b->sub1, privs, nrecurs, locktype)
		&& eval_boolexp(player, b->sub2, privs, nrecurs, locktype));
      case BOOLEXP_OR:
	return (eval_boolexp(player, b->sub1, privs, nrecurs, locktype)
		|| eval_boolexp(player, b->sub2, privs, nrecurs, locktype));
      case BOOLEXP_NOT:
	return !eval_boolexp(player, b->sub1, privs, nrecurs, locktype);
      case BOOLEXP_CONST:
	return (b->thing == player
		|| member(b->thing, db[player].contents));
      case BOOLEXP_IS:
	return (b->sub1->thing == player);
      case BOOLEXP_CARRY:
	return (member(b->sub1->thing,db[player].contents));
      case BOOLEXP_ATR:
	a = atr_complete_match(player, b->atr_lock->name, privs);
	if(!a)
	  return 0;
	strcpy(tbuf1, uncompress(b->atr_lock->text));
	strcpy(tbuf2, uncompress(a->value));
        return (int) wild_match(tbuf1, tbuf2);
      default:
	fprintf(stderr, "ERROR: Bad Boolexp type! (continuing)\n");
	report();
	return 0;
      }				/* switch */
    /* should never be reached */
    fprintf(stderr,
	    "** ERROR **  Broken lock type %d in object called by player %d",
	    locktype, player);
    return 0;
  }				/* else */
}

/* If the parser returns TRUE_BOOLEXP, you lose */
/* TRUE_BOOLEXP cannot be typed in by the user; use @unlock instead */
static const char *parsebuf;
static dbref parse_player;

static void skip_whitespace()
{
  while (*parsebuf && isspace(*parsebuf))
    parsebuf++;
}

static struct boolexp *parse_boolexp_E();	/* defined below */

static struct boolexp *test_atr(s)
    char *s;
{
  struct boolexp *b;
  char tbuf1[BUFFER_LEN];

  strcpy(tbuf1, s);
  for (s = tbuf1; *s && (*s != ':'); s++) ;
  if (!*s)
    return (0);
  *s++ = 0;
  b = alloc_bool();
  b->type = BOOLEXP_ATR;
  /* !!! Possible portability problem!!!!! */
  b->atr_lock = alloc_atr(tbuf1, s);
  return (b);
}

/* L -> (E); L -> object identifier */
static struct boolexp *parse_boolexp_L()
{
  struct boolexp *b;
  char *p;
  char tbuf1[BUFFER_LEN];

  skip_whitespace();
  switch (*parsebuf) {
    case '(':
      parsebuf++;
      b = parse_boolexp_E();
      skip_whitespace();
      if (b == TRUE_BOOLEXP || *parsebuf++ != ')') {
	free_boolexp(b);
	return TRUE_BOOLEXP;
      } else {
	return b;
      }
      /* break; */
    default:
      /* must have hit an object ref */
      /* load the name into our buffer */
      p = tbuf1;
      while (*parsebuf
	     && *parsebuf != AND_TOKEN
	     && *parsebuf != OR_TOKEN
	     && *parsebuf != ')') {
	*p++ = *parsebuf++;
      }
      /* strip trailing whitespace */
      *p-- = '\0';
      while (isspace(*p))
	*p-- = '\0';

      /* check for an attribute */
      b = test_atr(tbuf1);
      if (b)
	return (b);
      b = alloc_bool();
      b->type = BOOLEXP_CONST;

      /* do the match */
      init_match(parse_player, tbuf1, TYPE_THING);
      match_neighbor();
      match_possession();
      match_me();
      match_absolute();
      match_player();

      b->thing = match_result();

      if (b->thing == NOTHING) {
	notify(parse_player,
	       tprintf("I don't see %s here.", tbuf1));
	free_bool(b);
	return TRUE_BOOLEXP;
      } else if (b->thing == AMBIGUOUS) {
	notify(parse_player,
	       tprintf("I don't know which %s you mean!", tbuf1));
	free_bool(b);
	return TRUE_BOOLEXP;
      } else {
	return b;
      }
      /* break */
  }
}

/* C -> +Identifier ; C -> L */
static struct boolexp *parse_boolexp_C()
{
  struct boolexp *b2;
  skip_whitespace();
  if(*parsebuf == IN_TOKEN) {
    parsebuf++;
    b2 = alloc_bool();
    b2->type = BOOLEXP_CARRY;
    if(((b2->sub1 = parse_boolexp_L()) == TRUE_BOOLEXP) ||
       (b2->sub1->type != BOOLEXP_CONST)) {
      free_boolexp(b2);
      return(TRUE_BOOLEXP);
    } else
      return(b2);
  }
  return (parse_boolexp_L());
}

/* I -> =Identifier ; I -> C */
static struct boolexp *parse_boolexp_I()
{
  struct boolexp *b2;
  skip_whitespace();
  if(*parsebuf == IS_TOKEN) {
    parsebuf++;
    b2 = alloc_bool();
    b2->type = BOOLEXP_IS;
    if(((b2->sub1 = parse_boolexp_L()) == TRUE_BOOLEXP) ||
       (b2->sub1->type != BOOLEXP_CONST)){
      free_boolexp(b2);
      return(TRUE_BOOLEXP);
    } else
      return(b2);
    }
    return(parse_boolexp_C());
}

/* A -> @L; A -> I */
static struct boolexp *parse_boolexp_A()
{
  struct boolexp *b2;
  skip_whitespace();
  if(*parsebuf == AT_TOKEN) {
    parsebuf++;
    b2 = alloc_bool();
    b2->type = BOOLEXP_IND;
    if(((b2->sub1 = parse_boolexp_L()) == TRUE_BOOLEXP) ||
       (b2->sub1->type != BOOLEXP_CONST)) {
      free_boolexp(b2);
      return(TRUE_BOOLEXP);
    } else
      return (b2);
    }
    return(parse_boolexp_I());
}

/* F -> !F;F -> A */
static struct boolexp *parse_boolexp_F()
{
  struct boolexp *b2;
  skip_whitespace();
  if (*parsebuf == NOT_TOKEN) {
    parsebuf++;
    b2 = alloc_bool();
    b2->type = BOOLEXP_NOT;
    if ((b2->sub1 = parse_boolexp_F()) == TRUE_BOOLEXP) {
      free_boolexp(b2);
      return (TRUE_BOOLEXP);
    } else
      return (b2);
  }
  return (parse_boolexp_A());
}


/* T -> F; T -> F & T */
static struct boolexp *parse_boolexp_T()
{
  struct boolexp *b;
  struct boolexp *b2;
  if ((b = parse_boolexp_F()) == TRUE_BOOLEXP) {
    return b;
  } else {
    skip_whitespace();
    if (*parsebuf == AND_TOKEN) {
      parsebuf++;

      b2 = alloc_bool();
      b2->type = BOOLEXP_AND;
      b2->sub1 = b;
      if ((b2->sub2 = parse_boolexp_T()) == TRUE_BOOLEXP) {
	free_boolexp(b2);
	return TRUE_BOOLEXP;
      } else {
	return b2;
      }
    } else {
      return b;
    }
  }
}
/* E -> T; E -> T | E */
static struct boolexp *parse_boolexp_E()
{
  struct boolexp *b;
  struct boolexp *b2;
  if ((b = parse_boolexp_T()) == TRUE_BOOLEXP) {
    return b;
  } else {
    skip_whitespace();
    if (*parsebuf == OR_TOKEN) {
      parsebuf++;

      b2 = alloc_bool();
      b2->type = BOOLEXP_OR;
      b2->sub1 = b;
      if ((b2->sub2 = parse_boolexp_E()) == TRUE_BOOLEXP) {
	free_boolexp(b2);
	return TRUE_BOOLEXP;
      } else {
	return b2;
      }
    } else {
      return b;
    }
  }
}


struct boolexp *parse_boolexp(player, buf)
    dbref player;
    const char *buf;
{
  parsebuf = buf;
  parse_player = player;
  return parse_boolexp_E();
}