/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

/*
 MurkMUD++ - A Windows compatible, C++ compatible Merc 2.2 Mud.

 \author Jon A. Lambert
 \date 01/02/2007
 \version 1.5
 \remarks
  This source code copyright (C) 2005, 2006, 2007 by Jon A. Lambert
  All rights reserved.

  Use governed by the MurkMUD++ public license found in license.murk++
*/

#include "os.hpp"
#include "config.hpp"
#include "globals.hpp"
#include "database.hpp"
#include "world.hpp"

/*
print a series of warnings - do not exit
*/
void log_printf (const char * str, ...) {
  char buf[MAX_STRING_LENGTH];
  va_list args;

  va_start (args, str);
  vsnprintf (buf, sizeof buf, str, args);
  va_end (args);

  time_t tm = std::time(NULL);
  char * strtime = std::ctime (&tm);
  strtime[strlen (strtime) - 1] = '\0';
  std::cerr << strtime << " :: " << buf << std::endl;
  return;
}

/*
 * Reports a bug.
 */
void bug_printf (const char * str, ...)
{
  char buf[MAX_STRING_LENGTH];
  std::ofstream fp;
  va_list args;

  if (fpArea != NULL) {
    int iLine;
    std::ifstream::pos_type iChar;

    iChar = fpArea->tellg();
    fpArea->seekg(0);
    for (iLine = 0; fpArea->tellg() < iChar; iLine++) {
      while (fpArea->get() != '\n') ;
    }
    fpArea->seekg(iChar);

    snprintf (buf, sizeof buf, "[*****] FILE: %s LINE: %d", strArea.c_str(), iLine);
    log_printf(buf);

    fp.open ("shutdown.txt", std::ofstream::out | std::ofstream::app | std::ofstream::binary);
    if (fp.is_open()) {
      fp << buf << std::endl;
      fp.close();
    }
  }

  char buf2[MAX_STRING_LENGTH];
  snprintf (buf2, sizeof buf2, "[*****] BUG: %s", str);
  va_start (args, str);
  vsnprintf (buf, sizeof buf, buf2, args);
  va_end (args);

  log_printf(buf);

  fp.open (BUG_FILE, std::ofstream::out | std::ofstream::app | std::ofstream::binary);
  if (fp.is_open()) {
    fp << buf << std::endl;
    fp.close();
  }
  return;
}

/*
 * Reports a bug.
 */
void fatal_printf (const char * str, ...)
{
  char buf[MAX_STRING_LENGTH];
  va_list args;

  va_start (args, str);
  vsnprintf (buf, sizeof buf, str, args);
  va_end (args);
  bug_printf (buf);
  WIN32CLEANUP
//  g_db->shutdown();
  std::abort();
  return;
}

#ifdef WIN32
void win_errprint (const char * str)
{
  void* errmsg;
  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
    FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*) &errmsg, 0, NULL);
  bug_printf ("%s : %s.", str, (char*) errmsg);
  LocalFree(errmsg);
}
#endif

/*
 * Read and allocate space for a string from a file.
 */
std::string fread_string (std::ifstream & fp)
{
  std::string str;
  char c;

  /*
   * Skip blanks.
   * Read first char.
   */
  do {
    c = fp.get();
  }
  while (isspace (c));

  if (c == '~')
    return str;

  for (;;) {
    /*
     * Back off the char type lookup,
     *   it was too dirty for portability.
     *   -- Furey
     */
    switch (c) {
    default:
      try {
        str.append(1, c);
      } catch(...) {
        fatal_printf ("Fread_string: Maximum string size exceeded.");
      }
      break;
    case EOF:
      fatal_printf ("Fread_string: EOF");
      break;
    case '\n':
      try {
        str.append("\n\r");
      } catch(...) {
        fatal_printf ("Fread_string: Maximum string size exceeded.");
      }
      break;
    case '\r':
      break;
    case '~':
      return str;
    }
    c = fp.get();
  }
}

/*
 * Read a letter from a file.
 */
char fread_letter (std::ifstream & fp)
{
  char c;

  do {
    c = fp.get();
  }
  while (isspace (c));

  return c;
}

/*
 * Read a number from a file.
 */
int fread_number (std::ifstream & fp)
{
  int number;
  bool sign;
  char c;

  do {
    c = fp.get();
  }
  while (isspace (c));

  number = 0;

  sign = false;
  if (c == '+') {
    c = fp.get();
  } else if (c == '-') {
    sign = true;
    c = fp.get();
  }

  if (!isdigit (c)) {
    fatal_printf ("Fread_number: bad format.");
  }

  while (isdigit (c)) {
    number = number * 10 + c - '0';
    c = fp.get();
  }

  if (sign)
    number = 0 - number;

  if (c == '|')
    number += fread_number (fp);
  else if (c != ' ')
    fp.unget();

  return number;
}

/*
 * Read to end of line (for comments).
 */
void fread_to_eol (std::ifstream & fp)
{
  char c;

  do {
    c = fp.get();
  }
  while (c != '\n' && c != '\r');

  do {
    c = fp.get();
  }
  while (c == '\n' || c == '\r');

  fp.unget();
  return;
}

/*
 * Read one word (into static buffer).
 */
std::string fread_word (std::ifstream & fp)
{
  static char word[MAX_INPUT_LENGTH];
  char *pword;
  char cEnd;

  do {
    cEnd = fp.get();
  }
  while (isspace (cEnd));

  if (cEnd == '\'' || cEnd == '"') {
    pword = word;
  } else {
    word[0] = cEnd;
    pword = word + 1;
    cEnd = ' ';
  }

  for (; pword < word + MAX_INPUT_LENGTH; pword++) {
    *pword = fp.get();
    if (cEnd == ' ' ? isspace (*pword) : *pword == cEnd) {
      if (cEnd == ' ')
        fp.unget();
      *pword = '\0';
      return std::string(word);
    }
  }

  fatal_printf ("Fread_word: word too long.");
  return NULL;
}