/**************************************************************************
*  File: mail.c                                            Part of tbaMUD *
*  Usage: Internal funcs and player spec-procs of mudmail system.         *
*                                                                         *
*  All rights reserved.  See license for complete information.            *
*                                                                         *
*  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
*  By Jeremy Elson. Rewritten by Welcor.                                  *
**************************************************************************/

#include "conf.h"
#include "sysdep.h"
#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "db.h"
#include "interpreter.h"
#include "handler.h"
#include "mail.h"
#include "modify.h"

/* local (file scope) function prototypes */
static void postmaster_send_mail(struct char_data *ch, struct char_data *mailman, int cmd, char *arg);
static void postmaster_check_mail(struct char_data *ch, struct char_data *mailman, int cmd, char *arg);
static void postmaster_receive_mail(struct char_data *ch, struct char_data *mailman, int cmd, char *arg);
static int mail_recip_ok(const char *name);
static void write_mail_record(FILE *mail_file, struct mail_t *record);
static void free_mail_record(struct mail_t *record);
static struct mail_t *read_mail_record(FILE *mail_file);

static int mail_recip_ok(const char *name)
{
  int player_i, ret = FALSE;

  if ((player_i = get_ptable_by_name(name)) >= 0) {
    if (!IS_SET(player_table[player_i].flags, PINDEX_DELETED))
      ret = TRUE;
  }
  return ret;
}

static void free_mail_record(struct mail_t *record)
{
	if (record->body)
		free(record->body);
  free(record);
}

static struct mail_t *read_mail_record(FILE *mail_file)
{
  char line[READ_SIZE];
  long sender, recipient;
  time_t sent_time;
  struct mail_t *record;

  if (!get_line(mail_file, line))
  	return NULL;

  if (sscanf(line, "### %ld %ld %ld", &recipient, &sender, (long *)&sent_time) != 3) {
  	log("Mail system - fatal error - malformed mail header");
  	log("Line was: %s", line);
  	return NULL;
  }

  CREATE(record, struct mail_t, 1);

  record->recipient = recipient;
  record->sender = sender;
  record->sent_time = sent_time;
  record->body = fread_string(mail_file, "read mail record");

  return record;
}

static void write_mail_record(FILE *mail_file, struct mail_t *record)
{
	fprintf(mail_file, "### %ld %ld %ld\n"
	                   "%s~\n",
                     record->recipient,
                     record->sender,
                     (long)record->sent_time,
                     record->body );
}

/* int scan_file(none)
 * Returns false if mail file is corrupted or true if everything correct.
 *
 * This is called once during boot-up.  It scans through the mail file
 * and indexes all entries currently in the mail file. */
int scan_file(void)
{
  FILE *mail_file;
  int count = 0;
  struct mail_t *record;

  if (!(mail_file = fopen(MAIL_FILE, "r"))) {
    log("   Mail file non-existant... creating new file.");
    touch(MAIL_FILE);
    return TRUE;
  }

  record = read_mail_record(mail_file);

  while (record) {
    free_mail_record(record);
    record = read_mail_record(mail_file);
    count++;
  }

  fclose(mail_file);
 	log("   Mail file read -- %d messages.", count);
 	return TRUE;
}

/* int has_mail(long #1)
 * #1 - id number of the person to check for mail.
 * Returns true or false.
 *
 * A simple little function which tells you if the player has mail or not. */
int has_mail(long recipient)
{
  FILE *mail_file;
  struct mail_t *record;

  if (!(mail_file = fopen(MAIL_FILE, "r"))) {
    perror("read_delete: Mail file not accessible.");
    return FALSE;
  }

  record = read_mail_record(mail_file);

  while (record) {
  	if (record->recipient == recipient) {
  		free_mail_record(record);
  		fclose(mail_file);
  		return TRUE;
    }
    free_mail_record(record);
    record = read_mail_record(mail_file);
  }
  fclose(mail_file);
  return FALSE;
}

/* void store_mail(long #1, long #2, char * #3)
 * #1 - id number of the person to mail to.
 * #2 - id number of the person the mail is from.
 * #3 - The actual message to send.
 *
 * call store_mail to store mail.  (hard, huh? :-) )  Pass 3 arguments:
 * who the mail is to (long), who it's from (long), and a pointer to the
 * actual message text (char *). */
void store_mail(long to, long from, char *message_pointer)
{
  FILE *mail_file;
  struct mail_t *record;

  if (!(mail_file = fopen(MAIL_FILE, "a"))) {
    perror("store_mail: Mail file not accessible.");
    return;
  }
  CREATE(record, struct mail_t, 1);

  record->recipient = to;
  record->sender = from;
  record->sent_time = time(0);
  record->body = message_pointer;

  write_mail_record(mail_file, record);
  free(record); /* don't free the body */
  fclose(mail_file);
}

/* char *read_delete(long #1)
 * #1 - The id number of the person we're checking mail for.
 * Returns the message text of the mail received.
 *
 * Retrieves one messsage for a player. The mail is then discarded from
 * the file. Expects mail to exist. */
char *read_delete(long recipient)
{
  FILE *mail_file, *new_file;
  struct mail_t *record, *record_to_keep = NULL;
  char buf[MAX_STRING_LENGTH];

  if (!(mail_file = fopen(MAIL_FILE, "r"))) {
    perror("read_delete: Mail file not accessible.");
    return strdup("Mail system malfunction - please report this");
  }

  if (!(new_file = fopen(MAIL_FILE_TMP, "w"))) {
    perror("read_delete: new Mail file not accessible.");
    fclose(mail_file);
    return strdup("Mail system malfunction - please report this");
  }

  record = read_mail_record(mail_file);

  while (record) {
  	if (!record_to_keep && record->recipient == recipient) {
  		record_to_keep = record;
  		record = read_mail_record(mail_file);
  		continue; /* don't write and free this one just yet */
    }
    write_mail_record(new_file, record);
    free_mail_record(record);
    record = read_mail_record(mail_file);
  }

  if (!record_to_keep)
  	sprintf(buf, "Mail system error - please report");
  else {
    char *tmstr, *from, *to;

    tmstr = asctime(localtime(&record_to_keep->sent_time));
    *(tmstr + strlen(tmstr) - 1) = '\0';

    from = get_name_by_id(record_to_keep->sender);
    to = get_name_by_id(record_to_keep->recipient);

 		snprintf(buf, sizeof(buf),
             " * * * * tbaMUD Mail System * * * *\r\n"
             "Date: %s\r\n"
             "To  : %s\r\n"
             "From: %s\r\n"
             "\r\n"
             "%s",

             tmstr,
             to ? to : "Unknown",
             from ? from : "Unknown",
             record_to_keep->body ? record_to_keep->body : "No message" );

    free_mail_record(record_to_keep);
  }
  fclose(mail_file);
  fclose(new_file);

  remove(MAIL_FILE);
  rename(MAIL_FILE_TMP, MAIL_FILE);

  return strdup(buf);
}

/* spec_proc for a postmaster using the above routines.  By Jeremy Elson */
SPECIAL(postmaster)
{
  if (!ch->desc || IS_NPC(ch))
    return (0);			/* so mobs don't get caught here */

  if (!(CMD_IS("mail") || CMD_IS("check") || CMD_IS("receive")))
    return (0);

  if (no_mail) {
    send_to_char(ch, "Sorry, the mail system is having technical difficulties.\r\n");
    return (0);
  }

  if (CMD_IS("mail")) {
    postmaster_send_mail(ch, (struct char_data *)me, cmd, argument);
    return (1);
  } else if (CMD_IS("check")) {
    postmaster_check_mail(ch, (struct char_data *)me, cmd, argument);
    return (1);
  } else if (CMD_IS("receive")) {
    postmaster_receive_mail(ch, (struct char_data *)me, cmd, argument);
    return (1);
  } else
    return (0);
}

static void postmaster_send_mail(struct char_data *ch, struct char_data *mailman,
			  int cmd, char *arg)
{
  long recipient;
  char buf[MAX_INPUT_LENGTH], **mailwrite;

  if (GET_LEVEL(ch) < MIN_MAIL_LEVEL) {
    snprintf(buf, sizeof(buf), "$n tells you, 'Sorry, you have to be level %d to send mail!'", MIN_MAIL_LEVEL);
    act(buf, FALSE, mailman, 0, ch, TO_VICT);
    return;
  }
  one_argument(arg, buf);

  if (!*buf) {			/* you'll get no argument from me! */
    act("$n tells you, 'You need to specify an addressee!'",
	FALSE, mailman, 0, ch, TO_VICT);
    return;
  }
  if (GET_GOLD(ch) < STAMP_PRICE && GET_LEVEL(ch) < LVL_IMMORT) {
    snprintf(buf, sizeof(buf), "$n tells you, 'A stamp costs %d coin%s.'\r\n"
	    "$n tells you, '...which I see you can't afford.'", STAMP_PRICE,
            STAMP_PRICE == 1 ? "" : "s");
    act(buf, FALSE, mailman, 0, ch, TO_VICT);
    return;
  }
  if ((recipient = get_id_by_name(buf)) < 0 || !mail_recip_ok(buf)) {
    act("$n tells you, 'No one by that name is registered here!'",
	FALSE, mailman, 0, ch, TO_VICT);
    return;
  }
  act("$n starts to write some mail.", TRUE, ch, 0, 0, TO_ROOM);
  snprintf(buf, sizeof(buf), "$n tells you, 'I'll take %d coins for the stamp.'\r\n"
       "$n tells you, 'Write your message. (/s saves /h for help).'",
	  STAMP_PRICE);

  act(buf, FALSE, mailman, 0, ch, TO_VICT);

  if (GET_LEVEL(ch) < LVL_IMMORT)
    GET_GOLD(ch) -= STAMP_PRICE;

  SET_BIT_AR(PLR_FLAGS(ch), PLR_MAILING);	/* string_write() sets writing. */

  /* Start writing! */
  CREATE(mailwrite, char *, 1);
  string_write(ch->desc, mailwrite, MAX_MAIL_SIZE, recipient, NULL);
}

static void postmaster_check_mail(struct char_data *ch, struct char_data *mailman,
			  int cmd, char *arg)
{
  if (has_mail(GET_IDNUM(ch)))
    act("$n tells you, 'You have mail waiting.'", FALSE, mailman, 0, ch, TO_VICT);
  else
    act("$n tells you, 'Sorry, you don't have any mail waiting.'", FALSE, mailman, 0, ch, TO_VICT);
}

static void postmaster_receive_mail(struct char_data *ch, struct char_data *mailman,
			  int cmd, char *arg)
{
  char buf[256];
  struct obj_data *obj;
  int y;

  if (!has_mail(GET_IDNUM(ch))) {
    snprintf(buf, sizeof(buf), "$n tells you, 'Sorry, you don't have any mail waiting.'");
    act(buf, FALSE, mailman, 0, ch, TO_VICT);
    return;
  }
  while (has_mail(GET_IDNUM(ch))) {
    obj = create_obj(); 
    obj->item_number = 1; 
    obj->name = strdup("mail paper letter");
    obj->short_description = strdup("a piece of mail");
    obj->description = strdup("Someone has left a piece of mail here.");

    GET_OBJ_TYPE(obj) = ITEM_NOTE;
    for(y = 0; y < TW_ARRAY_MAX; y++)
      obj->obj_flags.wear_flags[y] = 0;
    SET_BIT_AR(GET_OBJ_WEAR(obj), ITEM_WEAR_TAKE);
    GET_OBJ_WEIGHT(obj) = 1;
    GET_OBJ_COST(obj) = 30;
    GET_OBJ_RENT(obj) = 10;
    obj->action_description = read_delete(GET_IDNUM(ch));

    if (obj->action_description == NULL)
      obj->action_description =
	strdup("Mail system error - please report.  Error #11.\r\n");

    obj_to_char(obj, ch);

    act("$n gives you a piece of mail.", FALSE, mailman, 0, ch, TO_VICT);
    act("$N gives $n a piece of mail.", FALSE, ch, 0, mailman, TO_ROOM);
  }
}

void notify_if_playing(struct char_data *from, int recipient_id) 
{ 
  struct descriptor_data *d; 

  for (d = descriptor_list; d; d = d->next) 
    if ((IS_PLAYING(d)) && (GET_IDNUM(d->character) == recipient_id) && (has_mail(GET_IDNUM(d->character)))) 
      send_to_char(d->character, "You have new mudmail from %s.\r\n", GET_NAME(from)); 
} 
