/* predicates.c */

#include "copyright.h"

/* Predicates for testing various conditions */

#include <stdio.h>
#include <varargs.h>
#include <ctype.h>
#include <string.h>
#include <time.h>

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

char *pronoun_substitute();

char *tprintf(va_alist)
va_dcl	
{
  static char buff[BUFFER_LEN];
  va_list args;
  char *fmt;
  
  va_start(args);
  fmt = va_arg(args, char *);
  
  (void)vsprintf(buff, fmt, args);
  buff[BUFFER_LEN - 1] = '\0';
  return (buff);
}

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

int could_doit(player, thing)
    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(player, thing, what, def, owhat, odef, awhat, loc)
    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", (int) db[player].name, (int) 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(player, thing, can_see_loc)
    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(who, what)
    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(who, what)
    dbref who;
    dbref what;
{
  return ((Typeof(what) == TYPE_EXIT && db[what].location == NOTHING)
	  || controls(who, what));
}

int can_pay_fees(who, pennies)
    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(who, pennies)
    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(who, cost)
    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(who, payment)
    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(who, cost)
    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(name)
    const char *name;
{
  while(name && *name && isspace(*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(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(*scan) && !isspace(*scan))) {	/* was isgraph(*scan) */
      return 0;
    }
  }

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

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

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

  return 1;
}

void sstrcat(string, app)
    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);
}


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

  const static char *subjective[3] = {"it", "she", "he"};
  const static char *possessive[3] = {"its", "her", "his"};
  const static 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(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(*str) && islower(result[0]))
      result[0] = toupper(result[0]);
    else if(islower(*str) && isupper(result[0]))
      result[0] = tolower(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(player, str, privs)
    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(&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(*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(player, exp, argv, cause)
    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(player,arg1)
	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(player, target, type, message, def)
  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(obj1, obj2)
  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 (p, thing)
  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;
}