#include "copyright.h"

/* commands which set parameters */
#include "os.h"

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

#ifdef COMPRESS
#define alloc_compressed(x) alloc_string(compress(x))
#else /* COMPRESS */
#define alloc_compressed(x) alloc_string(x)
#endif /* COMPRESS */

#define FREE_STRING(X) X = ((X) ? (free ((void *) (X)), NULL) : NULL)

static dbref match_controlled (dbref player, const char *name)
{
  dbref match;

  init_match (player, name, NOTYPE);
  match_everything ();

  match = noisy_match_result ();
  if (match != NOTHING && !controls (player, match)) {
    notify (player, "Permission denied.");
    return NOTHING;
  } else {
    return match;
  }
}

void do_name (dbref player, const char *name, char *newname)
{
  dbref thing;
  char *password;

  if ((thing = match_controlled (player, name)) != NOTHING) {
    /* check for bad name */
    if (*newname == '\0') {
      notify (player, "Give it what new name?");
      return;
    }

    /* check for renaming a player */
    if (Typeof (thing) == TYPE_PLAYER) {
      /* split off password */
      for (password = newname; *password && !isspace ((int)*password); password++);
      /* eat whitespace */
      if (*password) {
        *password++ = '\0';     /* terminate name */
        while (*password && isspace ((int)*password))
          password++;
      }

      /* check for null password */
      if (!*password) {
        notify (player,
          "You must specify a password to change a player name.");
        notify (player, "E.g.: name player = newname password");
        return;
      } else if (strcmp (db[thing].name, "guest") == 0) {
        notify (player, "You are only a guest here...no name changing.");
        return;
      } else if (!db[thing].password) {
        /* If original has no password, set one */
        db[thing].password = alloc_string (password);
      } else if (strcmp (password, db[thing].password)) {
        notify (player, "Incorrect password.");
        return;
      } else if (string_compare (newname, db[thing].name) &&
        !ok_player_name (newname)) {
        notify (player, "You can't give a player that name.");
        return;
      }
      /* everything ok, notify */
      writelog ("NAME CHANGE: %s(#%d) to %s\n",
        db[thing].name, thing, newname);
#ifdef PLAYER_LIST
      delete_player (thing);
      free ((void *) db[thing].name);
      db[thing].name = alloc_string (newname);
      add_player (thing);
      notify (player, "Name set.");
      return;
#endif  /* PLAYER_LIST */
    } else {
      if (!ok_name (newname)) {
        notify (player, "That is not a reasonable name.");
        return;
      }
    }

    /* everything ok, change the name */
    if (db[thing].name) {
      free ((void *) db[thing].name);
    }
    db[thing].name = alloc_string (newname);
    notify (player, "Name set.");
  }
}

void do_describe (dbref player, const char *name, const char *description)
{
  dbref thing;

  if ((thing = match_controlled (player, name)) != NOTHING) {
    if (db[thing].description) {
      free ((void *) db[thing].description);
    }
    db[thing].description = alloc_compressed (description);
    notify (player, "Description set.");
  }
}

void do_fail (dbref player, const char *name, const char *message)
{
  dbref thing;

  if ((thing = match_controlled (player, name)) != NOTHING) {
    if (db[thing].fail_message) {
      free ((void *) db[thing].fail_message);
    }
    db[thing].fail_message = alloc_compressed (message);
    notify (player, "Message set.");
  }
}

void do_success (dbref player, const char *name, const char *message)
{
  dbref thing;

  if ((thing = match_controlled (player, name)) != NOTHING) {
    if (db[thing].succ_message) {
      free ((void *) db[thing].succ_message);
    }
    db[thing].succ_message = alloc_compressed (message);
    notify (player, "Message set.");
  }
}

void do_osuccess (dbref player, const char *name, const char *message)
{
  dbref thing;

  if ((thing = match_controlled (player, name)) != NOTHING) {
    if (db[thing].osuccess) {
      free ((void *) db[thing].osuccess);
    }
    db[thing].osuccess = alloc_compressed (message);
    notify (player, "Message set.");
  }
}

void do_ofail (dbref player, const char *name, const char *message)
{
  dbref thing;

  if ((thing = match_controlled (player, name)) != NOTHING) {
    if (db[thing].ofail) {
      free ((void *) db[thing].ofail);
    }
    db[thing].ofail = alloc_compressed (message);
    notify (player, "Message set.");
  }
}

void do_lock (dbref player, const char *name, const char *keyname)
{
  dbref thing;
  struct boolexp *key;

  init_match (player, name, NOTYPE);
  match_everything ();

  switch (thing = match_result ()) {
  case NOTHING:
    notify (player, "I don't see what you want to lock!");
    return;
  case AMBIGUOUS:
    notify (player, "I don't know which one you want to lock!");
    return;
  default:
    if (!controls (player, thing)) {
      notify (player, "You can't lock that!");
      return;
    }
    break;
  }

  key = parse_boolexp (player, keyname);
  if (key == TRUE_BOOLEXP) {
    notify (player, "I don't understand that key.");
  } else {
    /* everything ok, do it */
    free_boolexp (db[thing].key);
    db[thing].key = key;
    notify (player, "Locked.");
  }
}

void do_unlock (dbref player, const char *name)
{
  dbref thing;

  if ((thing = match_controlled (player, name)) != NOTHING) {
    free_boolexp (db[thing].key);
    db[thing].key = TRUE_BOOLEXP;
    notify (player, "Unlocked.");
  }
}

void do_unlink (dbref player, const char *name)
{
  dbref exit;

  init_match (player, name, TYPE_EXIT);
  match_exit ();
  match_here ();
  if (Wizard (player)) {
    match_absolute ();
  }

  switch (exit = match_result ()) {
  case NOTHING:
    notify (player, "Unlink what?");
    break;
  case AMBIGUOUS:
    notify (player, "I don't know which one you mean!");
    break;
  default:
    if (!controls (player, exit)) {
      notify (player, "Permission denied.");
    } else {
      switch (Typeof (exit)) {
      case TYPE_EXIT:
        db[exit].location = NOTHING;
        notify (player, "Unlinked.");
        break;
      case TYPE_ROOM:
        db[exit].location = NOTHING;
        notify (player, "Dropto removed.");
        break;
      default:
        notify (player, "You can't unlink that!");
        break;
      }
    }
  }
}

void do_chown (dbref player, const char *name, const char *newobj)
{
  dbref thing;
  dbref owner;

  init_match (player, name, NOTYPE);
  match_everything ();

  if ((thing = noisy_match_result ()) == NOTHING) {
    return;
  } else if ((owner = lookup_player (newobj)) == NOTHING &&
    string_compare (newobj, "me")) {
    notify (player, "I couldn't find that player.");
  } else if (Typeof (thing) == TYPE_PLAYER) {
    notify (player, "Players always own themselves.");
  } else if (!Wizard (player) &&
    ((string_compare (newobj, "me") &&
        owner != player) || !(db[thing].flags & UNWANTED))) {
    notify (player, "Permission denied.");
  } else if (!Wizard (player) && !could_doit (player, thing)) {
    notify (player, "That item is locked.");
  } else {
    if (!string_compare (newobj, "me"))
      owner = player;
    db[thing].owner = owner;
    notify (player, "Owner changed.");
  }
}

void do_set (dbref player, const char *name, const char *flag)
{
  dbref thing;
  const char *p;
  object_flag_type f;

  /* find thing */
  if ((thing = match_controlled (player, name)) == NOTHING)
    return;

  /* move p past NOT_TOKEN if present */
  for (p = flag; *p && (*p == NOT_TOKEN || isspace ((int)*p)); p++);

  /* identify flag */
  if (*p == '\0') {
    notify (player, "You must specify a flag to set.");
    return;
  } else if (string_prefix ("LINK_OK", p)) {
    f = LINK_OK;
  } else if (string_prefix ("DARK", p)) {
    f = DARK;
  } else if (string_prefix ("STICKY", p)) {
    f = STICKY;
  } else if (string_prefix ("WIZARD", p) || string_prefix ("TINKER", p)) {
    f = WIZARD;
  } else if (string_prefix ("TEMPLE", p) || string_prefix ("JUNKPILE", p)) {
    f = TEMPLE;
  } else if (string_prefix ("HAVEN", p)) {
    f = HAVEN;
  } else if (string_prefix ("ABODE", p)) {
    f = ABODE;
  } else if (string_prefix ("UNWANTED", p)) {
    f = UNWANTED;
#ifdef ROBOT_MODE
  } else if (string_prefix ("ROBOT", p) || string_prefix ("BOT", p)) {
    if (Typeof (thing) == TYPE_PLAYER) {
      if (Wizard (player)) {
        writelog ("ROBOT: success %s(%d) %s %s(%d)\n",
          db[player].name, player,
          *flag == NOT_TOKEN ? "reset" : "set", db[thing].name, thing);
      } else {
        writelog ("ROBOT: failed %s(%d) %s %s(%d)\n",
          db[player].name, player,
          *flag == NOT_TOKEN ? "reset" : "set", db[thing].name, thing);
        notify (player,
#ifndef TINKER
          "Only a Wizard can designate a player robotic.");
#else   /* TINKER */
          "Only a Tinker can designate a player robotic.");
#endif  /* TINKER */
        return;
      }
    }
    f = ROBOT;
#endif  /* ROBOT_MODE */
  } else if (string_prefix ("TABULAR_WHO", p)) {
    f = TABULAR_WHO;
  } else if (string_prefix ("REVERSED_WHO", p)) {
    f = REVERSED_WHO;
#ifdef GENDER
  } else if (string_prefix ("MALE", p) || string_prefix ("FEMALE", p) ||
    string_prefix ("NEUTER", p) || string_prefix ("UNASSIGNED", p)) {
    if (Typeof (thing) != TYPE_PLAYER) {
      notify (player, "Sorry, only players have gender.");
      return;
    }
    if (thing != player && !Wizard (player)) {
      notify (player, "You can only give yourself a sex-change.");
      return;
    }
    db[thing].flags &= ~GENDER_MASK;
    if (string_prefix ("UNASSIGNED", p) || *flag == NOT_TOKEN) {
      db[thing].flags |= (GENDER_UNASSIGNED << GENDER_SHIFT);
      notify (player, "Gender set to unassigned.");
    } else if (string_prefix ("MALE", p)) {
      db[thing].flags |= (GENDER_MALE << GENDER_SHIFT);
      notify (player, "Gender set to male.");
    } else if (string_prefix ("FEMALE", p)) {
      db[thing].flags |= (GENDER_FEMALE << GENDER_SHIFT);
      notify (player, "Gender set to female.");
    } else if (string_prefix ("NEUTER", p)) {
      db[thing].flags |= (GENDER_NEUTER << GENDER_SHIFT);
      notify (player, "Gender set to neuter.");
    }
    return;
#endif /* GENDER */
#ifdef RESTRICTED_BUILDING
  } else if (string_prefix ("BUILDER", p) ||) {
    string_prefix ("CONSTRUCTOR", p)
      f = BUILDER;
#endif /* RESTRICTED_BUILDING */
  } else {
    notify (player, "I don't recognize that flag.");
    return;
  }

  /* check for restricted flag */
  if (!Wizard (player)
    && (f == TEMPLE
#ifdef RESTRICTED_BUILDING
      || f == BUILDER
#endif /* RESTRICTED_BUILDING */
      || f == DARK &&
      (Typeof (thing) != TYPE_ROOM && Typeof (thing) != TYPE_THING))) {
    notify (player, "Permission denied.");
    return;
  }
#ifdef GOD_PRIV
  if (!God (player) && f == DARK && Typeof (thing) == TYPE_PLAYER) {
    notify (player, "Permission denied.");
    return;
  }
#endif  /* GOD_PRIV */

  if (!Wizard (player) && f == DARK && Typeof (thing) == TYPE_THING &&
    db[thing].location != player) {
    notify (player, "You must be holding an object to set it DARK.");
    return;
  }
#ifdef GOD_PRIV
  if (!God (player) && (f == WIZARD)) {
#else
  if (!Wizard (player) && (f == WIZARD)) {
#endif  /* GOD_PRIV */
    writelog ("WIZARD: failed %s(%d) %s %s(%d)\n",
      db[player].name, player,
      *flag == NOT_TOKEN ? "reset" : "set", db[thing].name, thing);

    notify (player, "Permission denied.");
    return;
  }

  if (f == WIZARD) {
    writelog ("WIZARD: success %s(%d) %s %s(%d)\n",
      db[player].name, player,
      *flag == NOT_TOKEN ? "reset" : "set", db[thing].name, thing);
  }
#ifdef GOD_PRIV
  /* check for unGODding */
  if (f == WIZARD && God (thing)) {
    notify (player, "Gods can not me made mortal.");
    return;
  }
#else
  if (f == WIZARD && thing == player) {
    notify (player, "You cannot make yourself mortal!");
    return;
  }
#endif  /* GOD_PRIV */

  if (f == HAVEN && !(Typeof (thing) == TYPE_ROOM ||
      Typeof (thing) == TYPE_PLAYER)) {
    notify (player, "Only rooms or players can be HAVEN.");
    return;
  }

  if (f == UNWANTED && Typeof (thing) == TYPE_PLAYER) {
    notify (player, "You should always want yourself.");
    return;
  }

  if ((f == TABULAR_WHO || f == REVERSED_WHO) &&
    Typeof (thing) != TYPE_PLAYER) {
    notify (player, "Player preference flags can only be set on players.");
    return;
  }

  /* else everything is ok, do the set */
  if (*flag == NOT_TOKEN) {
    /* reset the flag */
    db[thing].flags &= ~f;
    notify (player, "Flag reset.");
  } else {
    /* set the flag */
    db[thing].flags |= f;
    notify (player, "Flag set.");
  }
}

#ifdef RECYCLE
/****************************************************************
 * do_recycle: Allow any player to give an item away...the item
 * is @chowned to the "Recycler" player, and if it's a thing,
 * it is @linked to the home of the "Recycler" as well.
 ****************************************************************/

void do_recycle (dbref player, const char *name)
{
  dbref thing, recip, i;
  char buf[BUFFER_LEN];

  if (!name || !*name) {
    notify (player, "You must specify an object to @recycle.");
    return;
  }

  if ((thing = match_controlled (player, name)) == NOTHING)
    return;

  if (Typeof (thing) == TYPE_PLAYER) {
    notify (player, "You can't @recycle players.");
    return;
  }

  if ((recip = lookup_player (RECYCLER)) == NOTHING) {
    notify (player, "There is no current Recycler.");
    return;
  }

  /* Okay - do it */
  db[thing].owner = recip;

  /* If its a thing, link it to the Recyclers home */
  if (Typeof (thing) == TYPE_THING) {
    db[thing].exits = db[recip].exits;
    moveto (thing, db[thing].exits);
  }

  /* Clear its strings */
  FREE_STRING (db[thing].name);
  FREE_STRING (db[thing].description);
  FREE_STRING (db[thing].fail_message);
  FREE_STRING (db[thing].ofail);
  FREE_STRING (db[thing].succ_message);
  FREE_STRING (db[thing].osuccess);
  sprintf (buf, "junk-%d", thing);
  db[thing].name = alloc_string (buf);

  /* unlock it */
  free_boolexp (db[thing].key);
  db[thing].key = TRUE_BOOLEXP;

  /* Make it worthless */
  db[thing].pennies = 1;

  /* Make it up for grabs */
  db[thing].flags |= UNWANTED;

  /* Tell player we did it */
  notify (player, "Object recycled.");
}

/****************************************************************
 * do_count: count things...
 *	players:	Number of objects owned, carried
 *	rooms:		number of exits from & to, number contents
 ****************************************************************/

void do_count (dbref player, const char *name)
{
  dbref thing, i, exit;
  char buf[BUFFER_LEN];
  int owned = 0, contents = 0, from = 0, to = 0, objects = 0, rooms =
    0, exits = 0;
  int rowned = 0, rplayers = 0, robjects = 0, rrooms = 0, rexits = 0;

  if (!name || !*name) {
    notify (player, "You must specify an object to @count.");
    return;
  }

  if ((thing = match_controlled (player, name)) == NOTHING)
    return;

  if (Typeof (thing) != TYPE_PLAYER && Typeof (thing) != TYPE_ROOM) {
    notify (player, "You can only @count people or rooms.\n");
    return;
  }

  if (!payfor (player, FIND_COST)) {
    notify (player, "You don't have enough pennies.");
    return;
  }

  switch (Typeof (thing)) {
  case TYPE_PLAYER:
    for (i = 0; i < db_top; i++) {
      if (db[i].location == thing)
        contents++;
      if (db[i].owner == thing) {
        switch (Typeof (i)) {
        case TYPE_THING:
          objects++;
          break;
        case TYPE_EXIT:
          exits++;
          break;
        case TYPE_ROOM:
          rooms++;
          break;
        }
        owned++;
      }
    }
    sprintf (buf,
      "%s owns %d objects (%d things, %d rooms, %d exits), %d carried.",
      unparse_object (player, thing), owned, objects, rooms, exits, contents);
    notify (player, buf);
    break;

  case TYPE_ROOM:
    for (i = 0; i < db_top; i++) {
      if (db[i].location == thing) {
        if (Typeof (i) == TYPE_EXIT) {
          to++;
        } else {
          contents++;
        }
      }
    }
    DOLIST (exit, db[thing].exits) {
      from++;
    }
    sprintf (buf, "%s has %d entrances, %d exits, %d objects.",
      unparse_object (player, thing), to, from, contents);
    notify (player, buf);

    if (!Wizard (player))
      break;

    for (i = 0; i < db_top; i++) {
      if (db[db[i].owner].location == thing) {
        switch (Typeof (i)) {
        case TYPE_EXIT:
          rexits++;
          break;
        case TYPE_ROOM:
          rrooms++;
          break;
        case TYPE_THING:
          robjects++;
          break;
        case TYPE_PLAYER:
          rplayers++;
          break;
        }
        rowned++;
      }
    }

    if (rplayers == 0) {
      sprintf (buf, "There are no players in %s.\n",
        unparse_object (player, thing));
    } else {
      sprintf (buf,
        "The %d player%s in %s own%s %d %s: %d %s, %d %s, %d %s\n.",
        rplayers, (rplayers == 1) ? "" : "s",
        unparse_object (player, thing), (rplayers == 1) ? "s" : "",
        rowned, "objects", robjects, "things", rrooms, "rooms",
        rexits, "exits");
    }

    notify (player, buf);

    break;
  }

}
#endif  /* RECYCLE */