/* predicates.c */
#include "os.h"
#include "copyright.h"

/* Predicates for testing various conditions */

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

char *tprintf (char *fmt, ...)
{
  static char buff[BUFFER_LEN];
  va_list args;

  va_start (args, fmt);
  (void) vsprintf (buff, fmt, args);
  va_end (args);
  buff[BUFFER_LEN - 1] = '\0';
  return (buff);

}

int can_link_to (dbref who, dbref where)
{
  return ((where >= 0 && where <= db_top) &&
    (controls (who, where) || (db[where].flags & LINK_OK)));
}

int could_doit (dbref player, dbref thing)
{
  /* no if puppet trys to get key */
  if ((Typeof (player) != TYPE_PLAYER) &&
    ((Typeof (thing) == TYPE_THING) && (db[thing].flags & THING_KEY) ||
      (Typeof (thing) == TYPE_EXIT) && (db[thing].flags & EXIT_KEY)))
    return 0;

  if (Typeof (thing) != TYPE_ROOM && db[thing].location == NOTHING)
    return 0;
  return (eval_boolexp (player, db[thing].key, thing, 0, BASICLOCK));
}

void
did_it (dbref player, dbref thing, char *what, char *def, char *owhat,
  char *odef, char *awhat, dbref loc)
{
  ATTR *d;
  char tbuf1[BUFFER_LEN], tbuf2[BUFFER_LEN];

  loc = (loc == NOTHING) ? db[player].location : loc;
  if (loc == NOTHING)
    return;
  /* message to player */
  if (what && *what) {
    d = atr_get (thing, what);
    if (d) {
      strcpy (tbuf2, uncompress (d->value));
      strcpy (tbuf1, pronoun_substitute (thing, tbuf2, thing));
      notify (player, tbuf1);
    } else if (def && *def)
      notify (player, def);
  }
  /* message to neighbors */
  if (!Dark (player)) {
    if (owhat && *owhat) {
      d = atr_get (thing, owhat);
      if (d) {
        strcpy (tbuf2, uncompress (d->value));
        strcpy (tbuf1, pronoun_substitute (player, tbuf2, thing));
        notify_except2 (db[loc].contents, player, thing,
          tprintf ("%s %s", db[player].name, tbuf1));
      } else {
        if (odef && *odef) {
          notify_except2 (db[loc].contents, player, thing,
            tprintf ("%s %s", db[player].name, odef));
        }
      }
    }
  }
  /* do the action attribute */
  if (awhat && *awhat && (d = atr_get (thing, awhat))) {
    ATTR *b;

    strcpy (tbuf1, uncompress (d->value));
    parse_que (thing, tbuf1, player);
    /* check if object has # of charges */
    b = atr_get (thing, "CHARGES");
    if (b) {
      int num = atoi (b->value);
      if (num) {
        (void) atr_add (thing, "CHARGES", tprintf ("%d", num - 1),
          db[b->creator].owner, NOTHING);
      } else if (!(d = atr_get (thing, "RUNOUT")))
        return;
      strcpy (tbuf2, uncompress (d->value));
      parse_que (thing, tbuf2, player);
    }
  }
}

int can_see (dbref player, dbref thing, int can_see_loc)
{
  /*
   * 1) your own body isn't listed in a 'look' 2) exits aren't listed in a
   * 'look' 3) unconnected (sleeping) players aren't listed in a 'look'
   */
  if (player == thing ||
    Typeof (thing) == TYPE_EXIT ||
    ((Typeof (thing) == TYPE_PLAYER) &&
      !IS (thing, TYPE_PLAYER, PLAYER_CONNECT)))
    return 0;

  /* if the room is lit, you can see any non-dark objects */
  else if (can_see_loc)
    return (!Dark (thing));

  /* otherwise room is dark and you can't see a thing */
  else
    return 0;
}

int controls (dbref who, dbref what)
{
  /* Wizard controls everything */
  /* owners control their stuff */
  return (what >= 0 && what <= db_top &&
    (Wizard (who) || (db[who].owner == db[what].owner) ||
      ((db[what].zone != NOTHING) &&
        (eval_boolexp (who, db[getzone (what)].enterkey, what,
            0, ENTERLOCK)))));
}

int can_link (dbref who, dbref what)
{
  return ((Typeof (what) == TYPE_EXIT && db[what].location == NOTHING)
    || controls (who, what));
}

int can_pay_fees (dbref who, int pennies)
{
#ifdef QUOTA
  int pay_quota ();
#endif /* QUOTA */

  /* can't charge till we've verified building quota */
  if (!Wizard (who) && !Wizard (db[who].owner) &&
    Pennies (db[who].owner) < pennies) {
    notify (who, tprintf ("Sorry, you don't have enough %s.", MONIES));
    return 0;
  }
  /* check building quota */
#ifdef QUOTA
  if (!pay_quota (who, QUOTA_COST)) {
    notify (who, "Sorry, your building quota has run out.");
    return 0;
  }
#endif /* QUOTA */
  /* charge */
  payfor (who, pennies);

  return 1;
}

void giveto (dbref who, dbref pennies)
{
  /* wizards don't need pennies */
  if (Wizard (who) || Wizard (db[who].owner))
    return;

  who = db[who].owner;
  s_Pennies (who, Pennies (who) + pennies);
}

int payfor (dbref who, int cost)
{
  dbref tmp;
  if (Wizard (who) || Wizard (db[who].owner))
    return 1;
  else if ((tmp = Pennies (db[who].owner)) >= cost) {
    s_Pennies (db[who].owner, tmp - cost);
    return 1;
  } else
    return 0;
}

#ifdef QUOTA
void add_quota (dbref who, int payment)
{
  ATTR *a;

  /* wizards don't need a quota */
  if (Wizard (who) || Wizard (db[who].owner))
    return;
  a = atr_get (who, "RQUOTA");
  (void) atr_add (who, "RQUOTA",
    tprintf ("%d", ((a) ?
        (atoi (uncompress (a->value))) + payment : payment)), GOD, NOTHING);
}

int pay_quota (dbref who, int cost)
{
  int quota = 0;
  ATTR *a;

  /* wizards don't need a quota */
  if (Wizard (who) || Wizard (db[who].owner))
    return 1;

  /* determine quota */
  a = atr_get (db[who].owner, "RQUOTA");
  if (a)
    quota = atoi (uncompress (a->value));

  /* enough to build? */
  quota -= cost;
  if (quota < 0)
    return 0;

  /* doc the quota */
  (void) atr_add (who, "RQUOTA", tprintf ("%d", quota), GOD, NOTHING);
  return 1;
}
#endif /* QUOTA */

int ok_name (const char *name)
{
  while (name && *name && isspace ((int)*name))
    name++;
  return (name
    && *name
    && *name != LOOKUP_TOKEN
    && *name != NUMBER_TOKEN
    && *name != NOT_TOKEN && !index (name, ARG_DELIMITER)
    && !index (name, AND_TOKEN)
    && !index (name, OR_TOKEN)
    && string_compare (name, "me")
    && string_compare (name, "home")
    && string_compare (name, "here"));
}

int ok_player_name (const char *name)
{
  const char *scan;
  if (!ok_name (name) || strlen (name) > PLAYER_NAME_LIMIT)
    return 0;

  for (scan = name; *scan; scan++) {
    if (!(isprint ((int)*scan) && !isspace ((int)*scan))) {       /* was isgraph(*scan) */
      return 0;
    }
  }

  return (lookup_player (name) == NOTHING);
}

int ok_password (const char *password)
{
  const char *scan;
  if (*password == '\0')
    return 0;

  for (scan = password; *scan; scan++) {
    if (!(isprint ((int)*scan) && !isspace ((int)*scan))) {
      return 0;
    }
  }

  return 1;
}

static void sstrcat (char *string, char *app)
{
  char *s;
  char tbuf1[BUFFER_LEN];

  if ((strlen (app) + strlen (string)) >= BUFFER_LEN)
    return;
  sprintf (tbuf1, "%s", app);
  for (s = tbuf1; *s; s++)
    if ((*s == ',') || (*s == ';'))
      *s = ' ';
  strcat (string, tbuf1);
}


static char *percent_sub (dbref player, char *str, dbref privs)
{

  static const char *subjective[3] = { "it", "she", "he" };
  static const char *possessive[3] = { "its", "her", "his" };
  static const char *objective[3] = { "it", "her", "him" };
  static char result[BUFFER_LEN];
  int gend;
  ATTR *a, *r;

  if (*str == '_') {
    r = atr_get (privs, str + 1);
    if (r) {
      if ((r->flags & AF_DARK) || ((r->flags & AF_WIZARD) && !Wizard (privs)))
        return (char *) "";
      sprintf (result, "%s", uncompress (r->value));
      return result;
    } else
      return (char *) "";
  } else {
    *result = '\0';

    switch (str[0]) {
    case 's':
    case 'S':
    case 'p':
    case 'P':
    case 'o':
    case 'O':
      a = atr_get (player, "SEX");
      if (!a)
        gend = 0;
      else {
        switch (*uncompress (a->value)) {
        case 'M':
        case 'm':
          gend = 2;
          break;
        case 'W':
        case 'w':
        case 'F':
        case 'f':
          gend = 1;
          break;
        default:
          gend = 0;
        }
      }
      break;
    default:
      gend = 0;
    }

    switch (str[0]) {
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      if (!wptr[str[0] - '0'])
        return (char *) "";
      else
        return (wptr[str[0] - '0']);
       /*NOTREACHED*/ break;
    case 's':
    case 'S':
      sprintf (result, "%s", subjective[gend]);
      break;
    case 'p':
    case 'P':
      sprintf (result, "%s", possessive[gend]);
      break;
    case 'o':
    case 'O':
      sprintf (result, "%s", objective[gend]);
      break;
    case 'n':
    case 'N':
      sprintf (result, db[player].name);
      break;
    case 'r':
    case 'R':
      return (char *) "\n";
       /*NOTREACHED*/ break;
    case 'b':
    case 'B':
      return (char *) " ";
       /*NOTREACHED*/ break;
    case 't':
    case 'T':
      return (char *) "\t";
       /*NOTREACHED*/ break;
    case 'v':
    case 'V':
    case 'w':
    case 'W':
    case 'x':
    case 'X':
      if (!isalpha ((int)str[1]))
        return str;
      r = atr_get (privs, str);
      if (r) {
        sprintf (result, "%s", uncompress (r->value));
        return result;
      } else
        return (char *) "";
       /*NOTREACHED*/ break;
    default:
      return str;
       /*NOTREACHED*/ break;
    }
    if (isupper ((int)*str) && islower ((int)result[0]))
      result[0] = toupper ((int)result[0]);
    else if (islower ((int)*str) && isupper ((int)result[0]))
      result[0] = tolower ((int)result[0]);
    return result;
  }
}

/*
 * pronoun_substitute()
 *
 * %-type substitutions for pronouns
 *
 * %s/%S for subjective pronouns (he/she/it, He/She/It) %o/%O for objective
 * pronouns (him/her/it, Him/Her/It) %p/%P for possessive pronouns
 * (his/her/its, His/Her/Its) %n/%N for the player's name. %r for a newline.
 * %<attrib_name> for the attribute value.
 */
char *pronoun_substitute (dbref player, const char *str, dbref privs    /* object whose privs are used */
  )
{
  static char buf[BUFFER_LEN];
  char temp[BUFFER_LEN];
  char *r, *q;

  if ((privs < 0) || privs >= db_top)
    return (char *) "";
  if ((player < 0) || player >= db_top)
    return (char *) "";

  if (strlen (str) > BUFFER_LEN - 1)
    return (char *) "";

  r = buf;
  *r = '\0';

  while (*str) {
    switch (*str) {
    case '[':
      str++;
      exec ((char**)&str, temp, privs, player, 0);
      if (strlen (buf) + strlen (temp) < BUFFER_LEN)
        strcat (buf, temp);
      r += strlen (temp);
      if (*str == ']')
        str++;
      break;
    case '%':
      switch (*(str + 1)) {
      case '%':
        if (r - buf < BUFFER_LEN - 1)
          *r++ = '%';
        str += 2;
        break;
      case '[':
        if (r - buf < BUFFER_LEN - 1)
          *r++ = '[';
        str += 2;
        break;
      case '#':
        str += 2;
        sprintf (temp, "#%d", player);
        sstrcat (buf, temp);
        r += strlen (temp);
        break;
      case '_':
        str += 2;
        temp[0] = '_';
        for (q = temp + 1;
          (*str && (isalnum ((int)*str) || *str == '_' || *str == '-'));
          *q++ = *str++);
        *q = '\0';
        q = percent_sub (player, temp, privs);
        sstrcat (buf, q);
        r += strlen (q);
        break;
      case '!':
        str += 2;
        sprintf (temp, "#%d", privs);
        sstrcat (buf, temp);
        r += strlen (temp);
        break;
      default:
        str++;                  /* strip off the leading % sign before calling %sub */
        temp[0] = *str;
        if (*str == 'v' || *str == 'w' || *str == 'x' ||
          *str == 'V' || *str == 'W' || *str == 'X') {
          str++;
          temp[1] = *str;
          temp[2] = '\0';
        } else
          temp[1] = '\0';
        str++;
        q = percent_sub (player, temp, privs);
        sstrcat (buf, q);
        r += strlen (q);
      }
      break;
    default:
      if (r - buf < BUFFER_LEN - 1) {
        *r++ = *str;
        *r = '\0';
      }
      str++;
    }
  }
  *r = '\0';
  return buf;
}

/* for lack of better place the @switch code is here */
void do_switch (dbref player, char *exp, char *argv[], dbref cause)
{
  int any = 0, a, c;
  char *ptrsrv[10];

  if (!argv[1])
    return;
  for (c = 0; c < 10; c++)
    ptrsrv[c] = wptr[c];
  /* now try a wild card match of buff with stuff in coms */
  for (a = 1; (a < (MAX_ARG - 1)) && argv[a] && argv[a + 1]; a += 2)
    if (wild_match (argv[a], exp)) {
      any = 1;
      for (c = 0; c < 10; c++)
        wptr[c] = ptrsrv[c];
      parse_que (player, argv[a + 1], cause);
    }
  for (c = 0; c < 10; c++)
    wptr[c] = ptrsrv[c];
  if ((a < MAX_ARG) && !any && argv[a])
    parse_que (player, argv[a], cause);
}

dbref is_possess (dbref player, char *arg1)
{
  dbref result1, result;
  char *place, *temp;

  if (!(place = index (arg1, ' ')))
    return NOTHING;
  temp = place;
  temp++;
  *place = '\0';
  place--;
  place--;
  if (*place != '\'')
    return NOTHING;
  *place = '\0';
  init_match (player, arg1, NOTYPE);
  match_neighbor ();
  match_possession ();
  result1 = match_result ();
  if (result1 == NOTHING || result1 == AMBIGUOUS)
    return result1;
  init_match (result1, temp, NOTYPE);
  match_possession ();
  result = match_result ();
  return result;
}

void
page_return (dbref player, dbref target, char *type, char *message, char *def)
{
  ATTR *d;
  char tbuf1[BUFFER_LEN], tbuf2[BUFFER_LEN];
  struct tm *ptr;
  time_t t;
  if (message && *message) {
    d = atr_get (target, message);
    if (d) {
      strcpy (tbuf2, uncompress (d->value));
      strcpy (tbuf1, pronoun_substitute (target, tbuf2, target));
      t = time (NULL);
      ptr = localtime (&t);
      notify (player, tprintf ("%s message from %s: %s", type,
          db[target].name, tbuf1));
      notify (target, tprintf ("[%d:%02d] %s message sent to %s.",
          ptr->tm_hour, ptr->tm_min, type, db[player].name));
    } else if (def && *def)
      notify (player, def);
  }
}

int nearby (dbref obj1, dbref obj2)
{
  /* returns 1 if obj1 is "nearby" object2. "Nearby" is defined as:  */
  /*   obj1 is in the same room as obj2, obj1 is being carried by    */
  /*   obj2, obj1 is carrying obj2. Returns 0 if object isn't nearby */
  /*   and -1 if the input is invalid.                               */

  if ((obj1 == NOTHING) || (obj2 == NOTHING))
    return -1;
  if ((Typeof (obj1) != TYPE_THING) && (Typeof (obj1) != TYPE_PLAYER))
    return -1;
  if ((Typeof (obj2) != TYPE_THING) && (Typeof (obj2) != TYPE_PLAYER))
    return -1;

  if (db[obj1].location == db[obj2].location)
    return 1;
  else if (db[obj1].location == obj2)
    return 1;
  else if (db[obj2].location == obj1)
    return 1;
  else
    return 0;
}

int find_flag (char *p, dbref thing)
{
  int f = 0;
  if (thing == NOTHING)
    return -1;
  switch (Typeof (thing)) {
  case TYPE_THING:
    if (string_prefix ("KEY", p))
      f = THING_KEY;
    if (string_prefix ("SAFE", p))
      f = THING_SAFE;
    if (string_prefix ("IMMORTAL", p))
      f = THING_IMMORTAL;
    if (string_prefix ("VERBOSE", p))
      f = THING_VERBOSE;
    if (string_prefix ("DESTROY_OK", p))
      f = THING_DEST_OK;
    if (string_prefix ("PUPPET", p))
      f = THING_PUPPET;
    if (string_prefix ("THING", p))
      f = TYPE_THING;
    break;
  case TYPE_EXIT:
    if (string_prefix ("KEY", p))
      f = EXIT_KEY;
    if (string_prefix ("TRANSPARENT", p))
      f = EXIT_TRANSPARENT;
    if (string_prefix ("EXIT", p))
      f = TYPE_EXIT;
    break;
  case TYPE_PLAYER:
#ifdef RESTRICTED_BUILDING
    if (string_prefix ("BUILDER", p))
      f = PLAYER_BUILD;
#endif /* RESTRICTED_BUILDING */
    if (string_prefix ("GAGGED", p))
      f = PLAYER_GAGGED;
    if (string_prefix ("SUSPECT", p))
      f = PLAYER_SUSPECT;
    if (string_prefix ("UNFINDABLE", p))
      f = PLAYER_DARK;
    if (string_prefix ("PLAYER", p))
      f = TYPE_PLAYER;
    if (string_prefix ("CONNECTED", p))
      f = PLAYER_CONNECT;
    break;
  case TYPE_ROOM:
    if (string_prefix ("TEMPLE", p))
      f = ROOM_TEMPLE;
    if (string_prefix ("ABODE", p))
      f = ROOM_ABODE;
    if (string_prefix ("JUMP_OK", p))
      f = ROOM_JUMP_OK;
    if (string_prefix ("LINK_OK", p))
      f = LINK_OK;
    if (string_prefix ("FLOATING", p))
      f = ROOM_FLOATING;
    if (string_prefix ("NO_TEL", p))
      f = ROOM_NO_TEL;
    if (string_prefix ("ROOM", p))
      f = TYPE_ROOM;
    break;
  }
  if (!f) {
#ifdef DESTROY
    if (string_prefix ("GOING", p)) {
      f = GOING;
#endif /* DESTROY */
    } else if (string_prefix ("DARK", p)) {
      f = DARK;
    } else if (string_prefix ("STICKY", p)) {
      f = STICKY;
    } else if (string_prefix ("WIZARD", p)) {
      f = WIZARD;
#ifdef ROYALTY_FLAG
    } else if (string_prefix ("ROYALTY", p)) {
      f = ROYALTY;
#endif
    } else if (string_prefix ("ENTER_OK", p)) {
      f = ENTER_OK;
    } else if (string_prefix ("CHOWN_OK", p)) {
      f = CHOWN_OK;
    } else if (string_prefix ("VISUAL", p)) {
      f = VISUAL;
    } else if (string_prefix ("OPAQUE", p)) {
      f = OPAQUE;
    } else if (string_prefix ("NOSPOOF", p)) {
      f = NOSPOOF;
#ifdef INHERIT_FLAG
    } else if (string_prefix ("INHERIT", p)) {
      f = INHERIT;
#endif
    } else if (string_prefix ("HAVEN", p)) {
      f = HAVEN;
    } else if (string_prefix ("HALT", p)) {
      f = HALT;
    } else if (string_prefix ("QUIET", p)) {
      f = QUIET;
    } else if (string_prefix ("TERSE", p)) {
      f = TERSE;
    } else {
      f = 0;
    }
  }
  return f;
}