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

/* Commands which involve speaking */
#include "config.h"
#include "externs.h"
#include "db.h"
#include "interface.h"
#include "match.h"

static void emit_notify_except (dbref first, dbref exception, const char *msg);
static void oemit_notify_except (dbref first, dbref exc1, dbref exc2, const char *msg);

static char *spname (dbref thing)
{
  static char buff[BUFFER_LEN];
#ifdef FULL_INVIS
  if (!Dark (thing)) {
    strcpy (buff, db[thing].name);
  } else {
    if (Typeof (thing) != TYPE_PLAYER) {
      strcpy (buff, "Something");
    } else {
      strcpy (buff, "Someone");
    }
  }
#else
  strcpy (buff, db[thing].name);
#endif
  return (buff);
}

/* this function is a kludge for regenerating messages split by '=' */
const char *reconstruct_message (const char *arg1, const char *arg2)
{
  static char buf[BUFFER_LEN];
  if (arg2 && *arg2) {
    strcpy (buf, arg1);
    strcat (buf, " = ");
    strcat (buf, arg2);
    return buf;
  } else {
    return arg1;
  }
}

void do_say (dbref player, const char *arg1, const char *arg2)
{
  dbref loc;
  const char *message;
  char tbuf1[BUFFER_LEN];

  if ((loc = getloc (player)) == NOTHING)
    return;
  message = reconstruct_message (arg1, arg2);
  strcpy (tbuf1, pronoun_substitute (player, message, player));
  /* notify everybody */
  notify (player, tprintf ("You say \"%s\"", tbuf1));
  notify_except (db[loc].contents, player,
    tprintf ("%s says \"%s\"", spname (player), tbuf1));
}

void do_oemit (dbref player, const char *arg1, const char *arg2)
{
  dbref who;
  dbref loc;
  char tbuf1[BUFFER_LEN];

  init_match (player, arg1, TYPE_PLAYER);
  if ((loc = getloc (player)) == NOTHING)
    return;
  match_neighbor ();
  match_me ();
  switch (who = match_result ()) {
  case NOTHING:
  case AMBIGUOUS:
    who = player;
  default:
    strcpy (tbuf1, pronoun_substitute (player, arg2, player));
    notify (player, tprintf ("%s", tbuf1));
    oemit_notify_except (db[loc].contents, player, who, tbuf1);
  }
}

void do_whisper (dbref player, const char *arg1, const char *arg2)
{
  dbref who;
  int key;
  const char *gap;
  init_match (player, arg1, TYPE_PLAYER);
  match_neighbor ();
  match_possession ();
  match_container ();
  match_me ();
  if (Wizard (player)) {
    match_absolute ();
    match_player ();
  }
  switch (who = match_result ()) {
  case NOTHING:
    notify (player, "Whisper to whom?");
    break;
  case AMBIGUOUS:
    notify (player, "I don't know who you mean!");
    break;
  default:
    gap = " ";
    switch (*arg2) {
    case SEMI_POSE_TOKEN:
      gap = "";
    case POSE_TOKEN:
      key = 1;
      arg2 = arg2 + 1;
      break;
    default:
      key = 2;
      break;
    }
    switch (key) {
    case 1:
      notify (player, tprintf ("%s senses, \"%s%s%s\"", db[who].name,
          db[player].name, gap, arg2));
      notify (who, tprintf ("You sense: %s%s%s", db[player].name, gap, arg2));
      break;
    case 2:
      notify (player,
        tprintf ("You whisper, \"%s\" to %s.", arg2, db[who].name));
      notify (who, tprintf ("%s whispers, \"%s\"", db[player].name, arg2));
      break;
    }
    break;
  }
}

void do_pemit (dbref player, const char *arg1, const char *arg2)
{
  dbref who;
  char tbuf1[BUFFER_LEN];

  init_match (player, arg1, NOTYPE);
  match_neighbor ();
  match_possession ();
  match_container ();
  match_me ();
  match_here ();
  match_player ();
  match_absolute ();
  switch (who = match_result ()) {
  case NOTHING:
    notify (player, "I don't see that player here.");
    break;
  case AMBIGUOUS:
    notify (player, "I don't know who you mean!");
    break;
  default:
    if (Typeof (who) != TYPE_PLAYER && Typeof (who) != TYPE_THING) {
      notify (player, "Only players and things can hear @pemits.");
      break;
    }
    if (Haven (who) && (player != who)) {
      notify (player,
        tprintf ("I'm sorry, but %s wishes to be left alone now.",
          db[who].name));
      return;
    }
    strcpy (tbuf1, pronoun_substitute (player, arg2, player));
    notify (player, tprintf ("You pemit \"%s\" to %s.", tbuf1, db[who].name));
    if (Nospoof (who)) {
      notify (who,
        tprintf ("[%s->%s] %s", db[player].name, db[who].name, tbuf1));
    } else {
      notify (who, tprintf ("%s", tbuf1));
    }
    break;
  }
}

void do_pose (dbref player, const char *arg1, const char *arg2, int space)
{
  dbref loc;
  const char *message;
  char tbuf1[BUFFER_LEN];

  if ((loc = getloc (player)) == NOTHING)
    return;

  message = reconstruct_message (arg1, arg2);
  strcpy (tbuf1, pronoun_substitute (player, message, player));
  /* notify everybody */
  if (!space)
    notify_except (db[loc].contents, NOTHING,
      tprintf ("%s %s", spname (player), tbuf1));
  else
    notify_except (db[loc].contents, NOTHING,
      tprintf ("%s%s", spname (player), tbuf1));
}

void do_wall (dbref player, const char *arg1, const char *arg2, int key)
{
  const char *gap;
  const char *message;
  if (!Wizard (player)) {
    notify (player, "Having delusions of grandeur today, are we?");
    return;
  }
  message = reconstruct_message (arg1, arg2);
  gap = " ";
  switch (*arg1) {
  case SAY_TOKEN:
    key = 1;
    message = message + 1;
    break;
  case SEMI_POSE_TOKEN:
    gap = "";
  case POSE_TOKEN:
    key = 2;
    message = message + 1;
    break;
  }
  switch (key) {
  case 2:
    fprintf (stderr, "WALL from %d: %s%s%s\n", player, db[player].name,
      gap, message);
    raw_broadcast (0, "Announcement: %s%s%s", db[player].name, gap, message);
    break;
  case 3:
    fprintf (stderr, "WALL from %s(%d): %s\n", db[player].name, player,
      message);
    raw_broadcast (0, "Announcement [%s]: %s", db[player].name, message);
    break;
  default:
    fprintf (stderr, "WALL from %s(%d): %s\n", db[player].name, player,
      message);
    raw_broadcast (0, "Announcement: %s shouts, \"%s\"", db[player].name,
      message);
    break;
  }
}

void
do_wizwall (dbref player, const char *arg1, const char *arg2, int privs,
  int key)
{
  /* privs is 0 for wizard wizwall, 1 for royalty-wizard wizwall */
  const char *gap;
  const char *message;

  if (!Wizard (player)
#ifdef ROYALTY_FLAG
    && !Royalty (player)
#endif
    ) {
    notify (player, "What makes you think someone wants to listen to you?");
    return;
  }

  if (!privs && !Wizard (player)) {
    notify (player, "Posing as a wizard could be hazardous to your health.");
    return;
  }

  message = reconstruct_message (arg1, arg2);
  gap = " ";
  switch (*arg1) {
  case SAY_TOKEN:
    key = 1;
    message = message + 1;
    break;
  case SEMI_POSE_TOKEN:
    gap = "";
  case POSE_TOKEN:
    key = 2;
    message = message + 1;
    break;
  }

  if (!privs) {
    switch (key) {
    case 2:
      raw_broadcast (WIZARD, "Broadcast: %s%s%s", db[player].name, gap,
        message);
      break;
    case 3:
      raw_broadcast (WIZARD, "Broadcast [%s]: %s", db[player].name, message);
      break;
    default:
      raw_broadcast (WIZARD, "Broadcast: %s says, \"%s\"", db[player].name,
        message);
    }
  }
#ifdef ROYALTY_FLAG
  else {
    switch (key) {
    case 2:
      raw_broadcast (WIZARD, "Admin: %s%s%s", db[player].name, gap, message);
      raw_broadcast (ROYALTY, "Admin: %s%s%s", db[player].name, gap, message);
      break;
    case 3:
      raw_broadcast (WIZARD, "Admin [%s]: %s", db[player].name, message);
      raw_broadcast (ROYALTY, "Admin [%s]: %s", db[player].name, message);
      break;
    default:
      raw_broadcast (WIZARD, "Admin: %s says, \"%s\"", db[player].name,
        message);
      raw_broadcast (ROYALTY, "Admin: %s says, \"%s\"", db[player].name,
        message);
    }
  }
#endif
}

void do_gripe (dbref player, const char *arg1, const char *arg2)
{
  dbref loc;
  const char *message;
  loc = db[player].location;
  message = reconstruct_message (arg1, arg2);
  fprintf (stderr, "GRIPE from %s(%d) in %s(%d): %s\n",
    db[player].name, player, db[loc].name, loc, message);
  fflush (stderr);

  /* try telling GOD about it */
  if (!Haven (GOD)) {
    notify (GOD,
      tprintf ("%s gripes: \"%s\"", unparse_object (GOD, player), message));
  }

  notify (player, "Your complaint has been duly noted.");
}

/* doesn't really belong here, but I couldn't figure out where else */

void do_page (dbref player, const char *arg1, const char *arg2)
{
  dbref target;
  const char *message, *gap;
  char spoof[1024];
  int key;
  if ((!*arg1) && (!*arg2)) {
    if (db[player].pageto == NOTHING) {
      notify (player, "You haven't paged anyone since connecting.");
    } else {
      notify (player,
        tprintf ("You last paged %s.", db[db[player].pageto].name));
    }
    return;
  }
  if (Haven (player))
    notify (player, "You are set HAVEN and cannot receive pages.");

  if (index (arg1, ' ') != NULL) {
    message = reconstruct_message (arg1, arg2);
    target = db[player].pageto;
  } else {
    message = arg2;
    if (!*arg1) {
      target = db[player].pageto;
    } else if ((target = lookup_player (arg1)) == NOTHING) {
      if (!*arg2) {
        target = db[player].pageto;
        message = arg1;
      } else {
        target = short_page (arg1);
      }
    }
  }
  if (target == NOTHING) {
    notify (player, "I can't find who you're trying to page.");
    return;
  } else if (target == AMBIGUOUS) {
    notify (player, "I'm not sure who you want to page!");
    return;
  } else if ((!(db[target].flags & PLAYER_CONNECT)) ||
    (Dark (target) && Haven (target))) {
    page_return (player, target, "Away", "AWAY",
      "That player is not connected.");
    return;
  } else if (Haven (target)) {
    page_return (player, target, "Haven", "HAVEN",
      "That player is not accepting any pages.");
    return;
  } else if (!eval_boolexp (db[player].owner, db[target].usekey,
      target, 0, USELOCK)) {
    page_return (player, target, "Haven", "HAVEN",
      "That player is not accepting your pages.");
    return;
  }
  if (!payfor (player, PAGE_COST)) {
    notify (player, tprintf ("You don't have enough %s.", MONIES));
    return;
  }
  db[player].pageto = target;
  gap = " ";
  switch (*message) {
  case SEMI_POSE_TOKEN:
    gap = "";
  case POSE_TOKEN:
    key = 1;
    message = message + 1;
    break;
  case 0:
    key = 2;
    break;
  default:
    key = 3;
    break;
  }

  if (Typeof (player) != TYPE_PLAYER && Nospoof (target))
    strcpy (spoof, tprintf ("[#%d] ", player));
  else
    strcpy (spoof, "");

  switch (key) {
  case 1:
    notify (target, tprintf ("%sFrom afar, %s%s%s", spoof,
        db[player].name, gap, message));
    notify (player, tprintf ("Long distance to %s: %s%s%s", db[target].name,
        db[player].name, gap, message));
    break;
  case 2:
    notify (target, tprintf ("%sYou sense that %s is looking for you in %s",
        spoof, db[player].name, db[db[player].location].name));
    notify (player, tprintf ("You have paged %s.", db[target].name));
    break;
  case 3:
    notify (target, tprintf ("%s%s pages: %s", spoof, db[player].name,
        message));
    notify (player, tprintf ("You paged %s with '%s'.", db[target].name,
        message));
    break;
  }
  page_return (player, target, "Idle", "IDLE", NULL);
  return;
}

static void esnotify (dbref player, const char *msg, dbref sender)
{
  if (player < 0 || player >= db_top)
    return;

  if (Nospoof (player)) {
    notify (player, tprintf ("[%s:] %s", spname (sender), msg));
  } else {
    notify (player, tprintf ("%s", msg));
  }
}

void notify_except (dbref first, dbref exception, const char *msg)
{
  if (first < 0 || first >= db_top)
    return;
  if (db[first].location != exception)
    notify (db[first].location, msg);
  DOLIST (first, first) {
    if (first != exception) {
      notify (first, msg);
    }
  }
}

static void emit_notify_except (dbref first, dbref exception, const char *msg)
{
  if (first < 0 || first >= db_top)
    return;
  if (db[first].location != exception)
    esnotify (db[first].location, msg, exception);
  DOLIST (first, first) {
    if (first != exception) {
      esnotify (first, msg, exception);
    }
  }
}

void notify_except2 (dbref first, dbref exc1, dbref exc2, const char *msg)
{
  if (first < 0 || first >= db_top)
    return;
  if ((db[first].location != exc1) && (db[first].location != exc2))
    notify (db[first].location, msg);
  DOLIST (first, first) {
    if (first != exc1 && first != exc2) {
      notify (first, msg);
    }
  }
}

static void
oemit_notify_except (dbref first, dbref exc1, dbref exc2, const char *msg)
{
  if (first < 0 || first >= db_top)
    return;
  if ((db[first].location != exc1) && (db[first].location != exc2))
    esnotify (db[first].location, msg, exc1);
  DOLIST (first, first) {
    if (first != exc1 && first != exc2) {
      esnotify (first, msg, exc1);
    }
  }
}

void do_emit (dbref player, const char *arg1, const char *arg2)
{
  dbref loc;
  const char *message;
  char tbuf1[BUFFER_LEN];

  if ((loc = getloc (player)) == NOTHING)
    return;
  message = reconstruct_message (arg1, arg2);
  strcpy (tbuf1, pronoun_substitute (player, message, player));
  /* notify everybody */
  notify (player, tprintf ("%s", tbuf1));
  emit_notify_except (db[loc].contents, player, tbuf1);
}

void do_remit (dbref player, const char *arg1, const char *arg2)
{
  dbref room;
  const char *rmno;
  char tbuf1[BUFFER_LEN];

  init_match (player, arg1, NOTYPE);
  match_here ();
  match_absolute ();
  match_neighbor ();
  match_me ();
  match_player ();
  match_exit ();

  switch (room = match_result ()) {
  case NOTHING:
  case AMBIGUOUS:
    notify (player, "I can't find that.");
    break;
  default:
    if (Typeof (room) == TYPE_EXIT) {
      notify (player, "There can't be anything in that!");
      break;
    }
    if ((Typeof (room) == TYPE_PLAYER) && Haven (room)) {
      notify (player, "That person is havened.  No @remitting allowed.");
      break;
    }
    strcpy (tbuf1, pronoun_substitute (player, arg2, player));
    rmno = unparse_object (player, room);
    notify (player, tprintf ("You remit, \"%s\" in %s", tbuf1, rmno));
    oemit_notify_except (db[room].contents, player, room, tbuf1);
  }
}

void do_lemit (dbref player, const char *arg1, const char *arg2)
{
  dbref room;
  int rec = 0;
  const char *message;
  char tbuf1[BUFFER_LEN];
  message = reconstruct_message (arg1, arg2);
  strcpy (tbuf1, pronoun_substitute (player, message, player));

  /* only players and things may use this command */
  if (Typeof (player) != TYPE_PLAYER && Typeof (player) != TYPE_THING)
    return;

  /* prevent infinite loop if player is inside himself */
  if ((room = db[player].location) == player) {
    notify (player, "Invalid container object.");
    return;
  }
  while ((Typeof (room) != TYPE_ROOM) && (rec < 15)) {
    room = db[room].location;
    rec++;
  }
  if (rec > 15) {
    notify (player, "Too many containers.");
    return;
  } else {
    notify (player, tprintf ("You lemit: \"%s\"", tbuf1));
    oemit_notify_except (db[room].contents, player, room, tbuf1);
  }
}

void do_zemit (dbref player, const char *arg1, const char *arg2)
{
  const char *where;
  dbref zone;
  dbref room;
  char tbuf1[BUFFER_LEN];

  init_match (player, arg1, NOTYPE);
  match_absolute ();

  switch (zone = match_result ()) {
  case NOTHING:
  case AMBIGUOUS:
    notify (player, "Invalid zone.");
    return;
  default:
    if (!controls (player, zone)) {
      notify (player, "Permission denied.");
      return;
    }
    if (!payfor (player, FIND_COST)) {
      notify (player, "Sorry, you don't have enough money to do that.");
      return;
    }
    strcpy (tbuf1, pronoun_substitute (player, arg2, player));
    where = unparse_object (player, zone);
    notify (player, tprintf ("You zemit, \"%s\" in zone %s", tbuf1, where));
    for (room = 0; room < db_top; room++)
      if ((getzone (room) == zone) && (Typeof (room) == TYPE_ROOM))
        oemit_notify_except (db[room].contents, player, room, tbuf1);
  }
}