/* boolexp.c */
#include "os.h"
#include "copyright.h"
#include "db.h"
#include "match.h"
#include "externs.h"
#include "config.h"
#include "interface.h"

int
eval_boolexp (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 (void)
{
  while (*parsebuf && isspace ((int)*parsebuf))
    parsebuf++;
}

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

static struct boolexp *test_atr (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 (void)
{
  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 ((int)*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 (void)
{
  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 (void)
{
  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 (void)
{
  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 (void)
{
  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 (void)
{
  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 (void)
{
  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 (dbref player, const char *buf)
{
  parsebuf = buf;
  parse_player = player;
  return parse_boolexp_E ();
}