#include "copyright.h"

#include "os.h"

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

int eval_boolexp (dbref player, struct boolexp *b)
{
  if (b == TRUE_BOOLEXP) {
    return 1;
  } else {
    switch (b->type) {
    case BOOLEXP_AND:
      return (eval_boolexp (player, b->sub1)
        && eval_boolexp (player, b->sub2));
    case BOOLEXP_OR:
      return (eval_boolexp (player, b->sub1)
        || eval_boolexp (player, b->sub2));
    case BOOLEXP_NOT:
      return !eval_boolexp (player, b->sub1);
    case BOOLEXP_CONST:
      return (b->thing == player || member (b->thing, db[player].contents));
    default:
      abort ();                 /* bad type */
      return 0;
    }
  }
}

/* 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 */

/* F -> (E); F -> !F; F -> object identifier */
static struct boolexp *parse_boolexp_F (void)
{
  struct boolexp *b;
  char *p;
  char buf[BUFFER_LEN];
  char msg[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; */
  case NOT_TOKEN:
    parsebuf++;
    b = (struct boolexp *) malloc (sizeof (struct boolexp));
    b->type = BOOLEXP_NOT;
    b->sub1 = parse_boolexp_F ();
    if (b->sub1 == TRUE_BOOLEXP) {
      free ((void *) b);
      return TRUE_BOOLEXP;
    } else {
      return b;
    }
    /* break */
  default:
    /* must have hit an object ref */
    /* load the name into our buffer */
    p = buf;
    while (*parsebuf
      && *parsebuf != AND_TOKEN
      && *parsebuf != OR_TOKEN && *parsebuf != ')') {
      *p++ = *parsebuf++;
    }
    /* strip trailing whitespace */
    *p-- = '\0';
    while (isspace ((int)*p))
      *p-- = '\0';

    b = (struct boolexp *) malloc (sizeof (struct boolexp));
    b->type = BOOLEXP_CONST;

    /* do the match */
    init_match (parse_player, buf, TYPE_THING);
    match_neighbor ();
    match_possession ();
    match_me ();
    match_absolute ();
    match_player ();
    b->thing = match_result ();

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

/* 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 = (struct boolexp *) malloc (sizeof (struct boolexp));
      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 = (struct boolexp *) malloc (sizeof (struct boolexp));
      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 ();
}