//  //
// #   #  ###  #   #              -= Nuclex Library =-                   //
// ##  # #   # ## ## String.cpp - Nuclex string class                    //
// ### # #      ###                                                      //
// # ### #      ###  Nuclex string class, extends std::string with a     //
// #  ## #   # ## ## few additional methods                              //
// #   #  ###  #   # R1        (C)2002-2004 Markus Ewald -> License.txt  //
//  //
#include "Nuclex/Support/String.h"
#include "Nuclex/Support/Exception.h"
#include "Nuclex/Storage/Stream.h"

#include <vector>
#include <locale>

using namespace Nuclex;
using namespace Nuclex::Support;
using namespace std;

namespace {

// The CRC32 table
const unsigned_32 CRC32Table[256] = {
  0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,
  0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD,
  0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75,
  0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, 0x709F7B7A, 0x745E66CD,
  0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039, 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5,
  0xBE2B5B58, 0xBAEA46EF, 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D,
  0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95,
  0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D,
  0x34867077, 0x30476DC0, 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072,
  0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, 0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA,
  0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE, 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02,
  0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA,
  0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B, 0xBB60ADFC, 0xB6238B25, 0xB2E29692,
  0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6, 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A,
  0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E, 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2,
  0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34, 0xDC3ABDED, 0xD8FBA05A,
  0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637, 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB,
  0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, 0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53,
  0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, 0x3F9B762C, 0x3B5A6B9B,
  0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF, 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623,
  0xF12F560E, 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B,
  0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3,
  0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B,
  0x9B3660C6, 0x9FF77D71, 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3,
  0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, 0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C,
  0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8, 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24,
  0x119B4BE9, 0x155A565E, 0x18197087, 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC,
  0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D, 0x2056CD3A, 0x2D15EBE3, 0x29D4F654,
  0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0, 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C,
  0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18, 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4,
  0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, 0x97FFAD0C,
  0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668, 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4
};

// ####################################################################### //
// # Nuclex::checkWildcardMatch()                                        # // 
// ####################################################################### //
/// Ascii wildcard match function
/** Compares a string to a wildcard

    @param  pszString    String to compare to the wildcard
    @param  pszWildcard  Wildcard specification to compare with
    @return True if the string matches the wildcard specification
*/
inline bool checkWildcardMatch(const char *pszString, const char *pszWildcard) {
  if(!pszString)
    return false;

  for(; '*' ^ *pszWildcard; ++pszWildcard, ++pszString) {
    if(!*pszString)
      return (!*pszWildcard);

    if(::toupper(*pszString) ^ ::toupper(*pszWildcard) && '?' ^ *pszWildcard)
      return false;
  }

  while('*' == pszWildcard[1])
    pszWildcard++;

  do {
    if(checkWildcardMatch(pszString, pszWildcard + 1))
      return true;

  } while(*pszString++);

  return false;
}

// ####################################################################### //
// # Nuclex::checkWildcardMatch()                                        # // 
// ####################################################################### //
/// Unicode wildcard match function
/** Compares a string to a wildcard

    @param  pszString    String to compare to the wildcard
    @param  pszWildcard  Wildcard specification to compare with
    @return True if the string matches the wildcard specification
*/
inline bool checkWildcardMatch(const wchar_t *pszString, const wchar_t *pszWildcard) {
  if(!pszString)
    return false;

  for(; L'*' ^ *pszWildcard; ++pszWildcard, ++pszString) {
    if(!*pszString)
      return (!*pszWildcard);

    if(::toupper(*pszString) ^ ::toupper(*pszWildcard) && L'?' ^ *pszWildcard)
      return false;
  }

  while(L'*' == pszWildcard[1])
    pszWildcard++;

  do {
    if(checkWildcardMatch(pszString, pszWildcard + 1))
      return true;

  } while(*pszString++);

  return false;
}

}

// ####################################################################### //
// # Nuclex::Support::unicodeFromAscii()                                 # //
// ####################################################################### //
/** Performs a string conversion from ascii to unicode

    @param  sAscii  Ascii string to be converted to unicode
    @return An unicode string which is equivalent to the ascii string
*/
wstring Nuclex::Support::unicodeFromAscii(const std::string &sAscii) {
  std::vector<wchar_t> Unicode(sAscii.length());
  const char *pszNext;
  wchar_t *pwszNext;

  mbstate_t state;
  int res = use_facet<codecvt<wchar_t, char, mbstate_t> >(locale("C")).in(
    state,
    sAscii.c_str(), &sAscii.c_str()[sAscii.length()], pszNext,
    &Unicode[0], &Unicode[0] + sAscii.length(), pwszNext
  );
  if(res == codecvt_base::error)
    throw UnexpectedException("Nuclex::Support::unicodeFromAscii()",
                              "Conversion failed");

  return std::wstring(&Unicode[0], pwszNext - &Unicode[0]);
}

// ####################################################################### //
// # Nuclex::Support::asciiFromUnicode()                                 # //
// ####################################################################### //
/** Performs a string conversion from unicode to ascii

    @param  sUnicode  Unicode string to be converted to ascii
    @return An ascii string which is equivalent to the unicode string
*/
string Nuclex::Support::asciiFromUnicode(const std::wstring &sUnicode) {
  std::vector<char> Ascii(sUnicode.length());
  char *pszNext;  
  const wchar_t *pwszNext;
  
  mbstate_t state;
  int res = use_facet<codecvt<wchar_t, char, mbstate_t> >(locale("C")).out(
    state,
    sUnicode.c_str(), &sUnicode.c_str()[sUnicode.length()], pwszNext,
    &Ascii[0], &Ascii[0] + sUnicode.length(), pszNext
  );
  if(res == codecvt_base::error)
    throw UnexpectedException("Nuclex::Support::asciiFromUnicode()",
                              "Conversion failed");

  return std::string(&Ascii[0], pszNext - &Ascii[0]);
}

// ####################################################################### //
// # Nuclex::Support::wildcardMatch()                                    # //
// ####################################################################### //
bool wildcardMatch(const string &sString, const string &sWildcard) {
  return checkWildcardMatch(sString.c_str(), sWildcard.c_str());
}

// ####################################################################### //
// # Nuclex::Support::wildcardMatch()                                    # //
// ####################################################################### //
bool wildcardMatch(const wstring &sString, const wstring &sWildcard) {
  return checkWildcardMatch(sString.c_str(), sWildcard.c_str());
}

// ####################################################################### //
// # Nuclex::Support::stringFromStream()                                 # //
// ####################################################################### //
/** Reads the entire contents of a stream into a string

    @param  spStream  Stream to be read
    @return A string containing the entire stream contents
*/
string Nuclex::Support::stringFromStream(const shared_ptr<Storage::Stream> &spStream) {
  std::vector<char> Chars(spStream->getSize());
  spStream->seekTo(0);
  spStream->readData(&Chars[0], spStream->getSize());
  
  return string(&Chars[0], spStream->getSize());
}  

// ####################################################################### //
// # Nuclex::Support::getCRC32()                                         # // 
// ####################################################################### //
/** Calculates a simple hashing value from the string

    @return CRC32 of the string
*/
unsigned_32 getCRC32(const void *pData, size_t Length) {
  const unsigned_8 *pcData = reinterpret_cast<const unsigned_8 *>(pData);

  unsigned_32 nAccum = 0;
  for(size_t nJ = 0; nJ < Length; nJ++) {
    unsigned_32 nI = (nAccum >> 24) ^ pcData[nJ] & 0xFF;
    nAccum = (nAccum << 8) ^ CRC32Table[nI];
  }

  return nAccum;
}

#if 0
// ####################################################################### //
// # Nuclex::Support::stringFromLong()                                   # //
// ####################################################################### //
/** Converts an integer into a string

    @param  nValue  Integer value to convert
    @param  nBase   Numeric base for the conversion
    @return The string representation of the integer
*/
string Nuclex::Support::stringFromLong(long nValue, size_t nBase) {
  char pszValue[64];
  ::itoa(nValue, pszValue, nBase);

  return pszValue; // Implicitely constructs a string
}

// ####################################################################### //
// # Nuclex::Support::stringFromFloat()                                  # //
// ####################################################################### //
/** Converts a float into a string

    @param  fValue     Float value to convert
    @param  nDecimals  Number of decimal places to return
    @return The string representation of the double
*/
string Nuclex::Support::stringFromFloat(float fValue, size_t nDecimals) {
  return stringFromDouble(fValue, nDecimals);
}

// ####################################################################### //
// # Nuclex::Support::stringFromDouble()                                 # //
// ####################################################################### //
/** Converts a double into a string

    @param  dValue     Double value to convert
    @param  nDecimals  Number of decimal places to return
    @return The string representation of the double
*/
string Nuclex::Support::stringFromDouble(double dValue, size_t nDecimals) {
  int nDec, nSign;

  string sTemp = _fcvt(dValue, nDecimals, &nDec, &nSign);
  if(nDec <= 0) {
    sTemp = string(1 - nDec, '0') + sTemp;
    nDec = 1;
  }

  if(nDecimals > 0) 
    return (nSign ? string("-") : string()) +
           (sTemp.substr(0, nDec) + "." + sTemp.substr(nDec)).c_str();
  else
    return (nSign ? string("-") : string()) + sTemp.substr(0, nDec);
}

// ####################################################################### //
// # Nuclex::Support::stringFromPointer()                                # //
// ####################################################################### //
/** Converts a pointer into a string

    @param  pPointer  Pointer to convert
    @return The string representation of the pointer
*/
string Nuclex::Support::stringFromPointer(const void *pPointer) {
  char pszTemp[16] = { '0', '0', '0', '0', '0', '0', '0', '0' };
  ::itoa(reinterpret_cast<int>(pPointer), &pszTemp[7], 16);

  return &pszTemp[11 - ::strlen(&pszTemp[7])]; // Implicitely constructs a string
}

#endif









/*
    CRC32Table() {
      unsigned_32 nI, nJ, nAccum;

      for(nI = 0; nI < 256; nI++) {
        nAccum = (nI << 24);

        for(nJ = 0; nJ < 8; nJ++) { 
          if(nAccum & 0x80000000L)
            nAccum = (nAccum << 1) ^ 0x04C11DB7L;
           else
            nAccum <<= 1;
        }
        data[nI] = nAccum;
      }
    }
*/
