/***************************************************************************
 *  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 08/30/2006
 \version 1.4
 \remarks
  This source code copyright (C) 2005, 2006 by Jon A. Lambert
  All rights reserved.

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

#include "os.hpp"
#include "config.hpp"

/*
 * Generate a random number in an inclusive range.
 */
int number_range (int from, int to)
{
  if (to <= from)
    return from;
  return (from + (OS_RAND () % (1 + to - from)));
}

/*
 * Stick a little fuzz on a number.
 */
int number_fuzzy (int number)
{
  switch (number_range (0, 3)) {
  case 0:
    number -= 1;
    break;
  case 3:
    number += 1;
    break;
  }

  return std::max (1, number);
}

/*
 * Roll some dice.
 */
int dice (int number, int size)
{
  int idice;
  int sum;

  switch (size) {
  case 0:
    return 0;
  case 1:
    return number;
  }

  for (idice = 0, sum = 0; idice < number; idice++)
    sum += number_range (1, size);

  return sum;
}

/*
 * Generate a percentile roll.
 */
int number_percent (void)
{
  return number_range (1, 100);
}

/*
 * Generate a random door.
 */
int number_door (void)
{
  return number_range (0, 5);
}

/*
 * Simple linear interpolation.
 */
int interpolate (int level, int value_00, int value_32)
{
  return value_00 + level * (value_32 - value_00) / 32;
}

// Case insensitive compare
bool str_cmp(const std::string & s1, const std::string & s2)
{
  if (s1.size() != s2.size())
    return true;

  std::string::const_iterator p1 = s1.begin(), p2 = s2.begin();
  while(p1 != s1.end() && p2 != s2.end()) {
    if(tolower(*p1) != tolower(*p2))
      return true;
    p1++;
    p2++;
  }
  return false;
}

/*
 * Compare strings, case insensitive, for prefix matching.
 * Return true if s1 not a prefix of bstr
 *   (compatibility with historical functions).
 */
bool str_prefix(const std::string & s1, const std::string & s2)
{
  if (s1.size() > s2.size())
    return true;

  std::string::const_iterator p1 = s1.begin(), p2 = s2.begin();
  while(p1 != s1.end() && p2 != s2.end()) {
    if(tolower(*p1) != tolower(*p2))
      return true;
    p1++;
    p2++;
  }
  return false;
}

/*
 * Compare strings, case insensitive, for match anywhere.
 * Returns true is astr not part of bstr.
 *   (compatibility with historical functions).
 */
bool str_infix (const std::string & astr, const std::string & bstr)
{
  if (astr.empty())
    return false;

  int cmpsz = bstr.size() - astr.size();
  char c0 = tolower(astr[0]);

  for (int ichar = 0; ichar <= cmpsz; ichar++) {
    if (c0 == tolower(bstr[ichar]) && !str_prefix(astr, bstr.substr(ichar)))
      return false;
  }

  return true;
}

/*
 * Compare strings, case insensitive, for suffix matching.
 * Return true if astr not a suffix of bstr
 *   (compatibility with historical functions).
 */
bool str_suffix(const std::string& s1, const std::string& s2)
{
  if (s1.size() <= s2.size() && !str_cmp(s1, s2.substr(s2.size() - s1.size())))
    return false;
  else
    return true;
}

/*
 * Returns an initial-capped string.
 */
std::string capitalize (const std::string & str)
{
  std::string strcap;

  std::string::const_iterator p = str.begin();
  while(p != str.end()) {
    if (p == str.begin())
      strcap.append(1, (char)toupper(*p));
    else
      strcap.append(1, (char)tolower(*p));
    p++;
  }
  return strcap;
}

// replaces all occurances of a s1 in str with s2
void global_replace (std::string & str, const std::string & s1, const std::string & s2)
{
  std::string::size_type pos = 0;

  while ((pos = str.find(s1, pos)) != std::string::npos) {
    str.replace(pos, s1.size(), s2);
    pos += s2.size();
  }
}

/*
 * Return true if an argument is completely numeric.
 */
bool is_number (const std::string & arg)
{
  if (arg.empty())
    return false;

  std::string::const_iterator p = arg.begin();
  if (*p == '+' || *p == '-')
    p++;

  for (; p != arg.end(); p++) {
    if (!isdigit (*p))
      return false;
  }

  return true;
}

/*
 * Given a string like 14.foo, return 14 and 'foo'
 */
int number_argument (const std::string & argument, std::string & arg)
{
  std::string::size_type pos;

  arg.erase();
  if ((pos = argument.find(".")) != std::string::npos) {
    arg = argument.substr(pos+1);
    return std::atoi (argument.substr(0, pos).c_str());
  }
  arg = argument;
  return 1;
}

/*
 * Pick off one argument from a string and return the rest.
 * Understands quotes.
 */
std::string one_argument (const std::string & argument, std::string & arg_first)
{
  char cEnd;
  std::string::const_iterator argp = argument.begin();

  arg_first.erase();
  while (argp != argument.end() && isspace (*argp))
    argp++;

  cEnd = ' ';
  if (*argp == '\'' || *argp == '"')
    cEnd = *argp++;

  while (argp != argument.end()) {
    if (*argp == cEnd) {
      argp++;
      break;
    }
    arg_first.append(1, (char)tolower(*argp));
    argp++;
  }

  while (argp != argument.end() && isspace (*argp))
    argp++;

  return std::string(argp, argument.end());
}

/*
 * See if a string is one of the names of an object.
 */
/*
 * New is_name sent in by Alander.
 */
bool is_name (const std::string & str, std::string namelist)
{
  std::string name;

  for (;;) {
    namelist = one_argument (namelist, name);
    if (name.empty())
      return false;
    if (!str_cmp (str, name))
      return true;
  }
}

/*
 * Removes the tildes from a string.
 * Used for player-entered strings that go into disk files.
 */
void smash_tilde (std::string & str)
{
  global_replace(str, "~", "-");
}