#ifndef BABBLEMUD_STRINGS_H
#define BABBLEMUD_STRINGS_H

#include <string>
#include <map>
#include <iterator>

using namespace std;

#include <ctype.h>   // for toupper, tolower
#include "utils.h"   // for iseq

extern "C"
  {
  #include "shs.h"
  }

// strings.h - string functions

static const string SPACES = " \t\r\n";           // what gets removed when we trim

// case-independent (ci) string compare
// returns true if strings are EQUAL
struct ciEqualTo : binary_function <string, string, bool>
  {
  struct compare_equal 
    : public binary_function <unsigned char, unsigned char,bool>
    {
    bool operator() (const unsigned char& c1, const unsigned char& c2) const
      { return tolower (c1) == tolower (c2); }
    };  // end of compare_equal

  bool operator() (const string & s1, const string & s2) const
    {
    pair <string::const_iterator, string::const_iterator> result =
      mismatch (ISEQ (s1), s2.begin (), compare_equal ()); 

    // match if both at end
    return result.first == s1.end () && result.second == s2.end ();
    }
  }; // end of ciEqualTo

// case-independent (ci) string less_than
// returns true if s1 < s2
struct ciLess : binary_function <string, string, bool>
  {
  // case-independent (ci) compare_less binary function
  struct compare_less 
    : public binary_function <unsigned char, unsigned char,bool>
    {
    bool operator() (const unsigned char& c1, const unsigned char& c2) const
      { return tolower (c1) < tolower (c2); }
    }; // end of compare_less

  bool operator() (const string & s1, const string & s2) const
    {
    return lexicographical_compare
          (ISEQ (s1), s2.begin (), s2.end (), compare_less ()); 
    }
  }; // end of ciLess

// prototypes
  
// find and replace in a string
const string FindAndReplace (const string& source, const string target, const string replacement);
  
// get rid of leading and trailing spaces from a string
const string trim (const string & s, const string & t = SPACES);
const string trim_right (const string & s, const string & t = SPACES);
const string trim_left (const string & s, const string & t = SPACES);

// convert to lowercase
const string tolower (const string & s);

// capitalise a string
const string tocapitals (const string & s);
// make first letter caps
const string firstLetterCaps (const string & s);

// case-independent compare equal  
bool ciStringEqual (const string & s1, const string & s2);
  
// split a string into first word, rest-of-line
const pair<string, string> GetWord (const string & s, const string & delim = " ");

// faster way of searching a string for a character that does not match
// warning - you need a string terminator, such as a 0x00
// returns NULL if end-of-string reached
struct find_first_not_of
  {

  // each entry will be true if the condition is matched
  unsigned char t [1 << CHAR_BIT];

  find_first_not_of (const string & s)  // constructor
    {
    memset (t, 0, sizeof (t));
    for (string::size_type i = 0; i < s.size (); i++)
      t [s [(unsigned char) i]] = true;
    };  

  inline const char * operator() (const char * p) const
    {
    while (t [*p])
      p++;

    if (*p)
      return p;
    else
      return NULL;
    }

  }; // end of find_first_not_of

// faster way of searching a string for a character that matches
// warning - you need a string terminator, such as a 0x00
// returns NULL if end-of-string reached
struct find_first_of
  {

  // each entry will be true if the condition is matched
  unsigned char t [1 << CHAR_BIT];

  find_first_of (const string & s)  // constructor
    {
    memset (t, 0, sizeof (t));
    for (string::size_type i = 0; i < s.size (); i++)
      t [s [(unsigned char) i]] = true;
    };  

  inline const char * operator() (const char * p) const
    {
    while (*p && !t [*p])
      p++;

    if (*p)
      return p;
    else
      return NULL;
    }

  }; // end of find_first_of

// used to scan a sequence (eg. of bad player names) to
//  see if any elements the sequence form part of the target
//   (eg. to disallow admin123)
struct stringContains : unary_function<const string &, bool>
  {
  const string & target_;
  
  stringContains (const string & target) : target_ (target) {}
  bool operator() (const string & s) const
     {
     return target_.find (s) != string::npos;
     }
  };  // end of stringContains

// compare to see if start of s1 is s2
//  eg. returns true for: strPrefix ("abacus", "aba");
const bool strPrefix (const string & s1,     // string to search    
                const string & s2,     // what to look for 
                const bool no_case = true);   // case-insensitive?   

// compares to see if (s1, offset by pos, for length s2) == s2
const bool strPrefix (const string & s1,              // string to search
                const string::size_type pos,    // where in s1 to start
                const string & s2,              // what to look for
                const bool no_case = true);     // case-insensitive?

char non_printables_to_spaces (const char & c);

// - is a string a simple number (signed or unsigned)
const bool IsNumber (const string & s, const bool bSigned = false);
// - is a string a hex number 
const bool IsHex (const string & s);

// returns the current date/time as a human-readable string
const string stringDate (const time_t & t);
const string stringShortDate (const time_t & t);
const string stringFileDate (const time_t & t);
const string makeHeading (const string & text,            // eg. "Help"
                          const char filler = '-',        // eg. '-'
                          const string & textColour = "", // eg. "`C"
                          const int width = 78);          // how wide
// generates a unique string ID
const string UniqueID ();
const string URLDecode (const string & sInput);
const string FixHTML (const string & sInput);
const string nl2br (const string & sInput);

// for generating secure hashes of a string
struct SecureHash : public SHS_INFO
  {
  // ctor
  SecureHash (const string & s = ""); 
  // hash a supplied string
  void hash (const string & s);
  // return result in hex
  const string str ();
  };  // end of SecureHash

extern ::find_first_not_of fo_find_not_name;

// To be symmetric, we assume an empty string (after trimming spaces)
// will give an empty vector.
// However, a non-empty string (with no delimiter) will give one item
// After that, you get an item per delimiter, plus 1.
// eg.  ""      => empty
//      "a"     => 1 item
//      "a,b"   => 2 items
//      "a,b,"  => 3 items (last one empty)

template <typename T>
void stringToContainer (const string & s, 
                       T & v,
                       const string & delim = ",", 
                       const bool trim_spaces = true)
  {

  // start with initial string, trimmed of leading/trailing spaces if required
  string s1 (trim_spaces ? trim (s) : s);

  v.clear (); // ensure container empty

  // no string? no elements
  if (s1.empty ())
    return;

  // add to container while we have a delimiter
  while (!s1.empty () && s1.find (delim) != string::npos)
    {
    pair<string, string> w = GetWord (s1, delim);
    inserter (v, v.end ()) = w.first;
    s1 = w.second;
    }

  // add final element
  inserter (v, v.end ()) = s1;
  } // end of stringToContainer 

// Takes a vector of strings and converts it to a string 
// like "apples,peaches,pears" 
// Should be symmetric with StringToVector (excepting any spaces that might have
//  been trimmed).

template <typename T>
string containerToString (const T & v, 
                          const string & delim = ",")
  {
  // container empty gives empty string
  if (v.empty ())
    return "";

  // for copying results into
  ostringstream os;

  // copy all with delimiter after each one
  copy (ISEQ (v), ostream_iterator<string> (os, delim.c_str ()));

  string s = os.str ();
  
  // return string with final delimiter removed
  return s.substr (0, s.size () - delim.size ());

  } // end of containerToString

typedef map<string, string> StringMap;
typedef map<string, string, ciLess> CiStringMap;

// this maps messages (eg. errors) to a language, eg. english
typedef map<string, StringMap> LanguageMap;

#endif // BABBLEMUD_STRINGS_H

