// ZenLib::Ztring - std::(w)string is better
// Copyright (C) 2002-2005 Jerome Martinez, Zen@MediaArea.net
//
// This software is provided 'as-is', without any express or implied
// warranty.  In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//    misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// More methods for std::(w)string
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// 2005-10-11, Zen@MediaArea.net
// assign instead of append in some From_Number(methods)
//
// 2005-07-18, Zen@MediaArea.net
// From_Number, Option : Ztring_NoZero
//
// 2005-03-15, Zen@MediaArea.net
// "Size" in From_XXX
// Count()
// Compare()
//
// 2005-03-08, Zen@MediaArea.net
// Delete automatic integers translation
// UTF8 and local encoded handling in Ansi version
//
// 2005-02-26, Zen@MediaArea.net
// If UNICODE, support of char entry
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//---------------------------------------------------------------------------
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <sstream>
#include <iomanip>
#include <math.h>
#if defined(__BORLANDC__)
    #include <mem.h> //Needed for wx
#endif
#include <wx/strconv.h>
#include <ZenLib/Ztring.h>
#include <ZenLib/Utils.h>
using namespace ZenLib;
using namespace std;
//---------------------------------------------------------------------------

namespace ZenLib
{

//---------------------------------------------------------------------------
typedef basic_stringstream<Char> tstringstream;
//---------------------------------------------------------------------------

//***************************************************************************
// Operators
//***************************************************************************

Char &Ztring::operator () (size_t Pos)
{
    if (Pos>size())
        resize (Pos);
    return operator[] (Pos);
}

//***************************************************************************
// Conversions
//***************************************************************************

Ztring& Ztring::From_Unicode (const wchar_t* S)
{
    #ifdef _UNICODE
        assign(S);
    #endif
    return *this;
}

Ztring& Ztring::From_Unicode (const wchar_t* S, size_t Length)
{
    wchar_t* Temp=new wchar_t[Length+1];
    wcsncpy (Temp, S, Length);
    Temp[Length]='\0';
    #ifdef _UNICODE
        assign(Temp);
    #endif
    delete[] Temp;
    return *this;
}

Ztring& Ztring::From_UTF8 (const char* S)
{
        size_t OK=wxConvUTF8.MB2WC(NULL, S, 0);
        if (OK!=0 && OK!=Error)
        #ifdef _UNICODE
            assign(wxConvUTF8.cMB2WC(S).data());
        #else
            assign(wxConvLocal.cWC2MB(wxConvUTF8.cMB2WC(S)));
        #endif
    return *this;
}

Ztring& Ztring::From_UTF8 (const char* S, size_t Length)
{
    #ifdef _UNICODE
        char* Temp=new char[Length+1];
        strncpy (Temp, S, Length);
        Temp[Length]='\0';
        From_UTF8(Temp);
        delete[] Temp;
    #else
        char* Temp=new char[Length+1];
        strncpy (Temp, S, Length);
        Temp[Length]='\0';
        From_UTF8(Temp);
        delete[] Temp;
    #endif
    return *this;
}

Ztring& Ztring::From_Local (const char* S)
{
    #ifdef _UNICODE
        size_t OK=wxConvLocal.MB2WC(NULL, S, 0);
        if (OK!=0 && OK!=Error)
            assign(wxConvLocal.cMB2WC(S).data());
    #else
        assign(S);
    #endif
    return *this;
}

Ztring& Ztring::From_Local (const char* S, size_t Length)
{
    char* Temp=new char[Length+1];
    strncpy (Temp, S, Length);
    Temp[Length]='\0';
    #ifdef _UNICODE
        From_Local(Temp);
    #else
        assign(Temp);
    #endif
    delete[] Temp;
    return *this;
}

Ztring& Ztring::From_Number (const int8s I, intu Radix)
{
    #if defined (__MINGW32__) && defined (_UNICODE) // MinGW32 does'nt support wide streams 
        wchar_t* C1=new wchar_t[33];
        _itow (I, C1, Radix);
        assign (C1);
        delete[] C1;
    #else
        tstringstream SS;
        SS << setbase(Radix) << (int)I;
        assign(SS.str());
    #endif
    return *this;
}

Ztring& Ztring::From_Number (const int8u I, intu Radix)
{
    #if defined (__MINGW32__) && defined (_UNICODE) // MinGW32 does'nt support wide streams 
        wchar_t* C1=new wchar_t[33];
        _ultow (I, C1, Radix);
        assign (C1);
        delete[] C1;
    #else
        tstringstream SS;
        SS << setbase(Radix) << (unsigned int)I;
        assign(SS.str());
    #endif
    return *this;
}

Ztring& Ztring::From_Number (const int16s I, intu Radix)
{
    #if defined (__MINGW32__) && defined (_UNICODE) // MinGW32 does'nt support wide streams 
        wchar_t* C1=new wchar_t[33];
        _itow (I, C1, Radix);
        assign (C1);
        delete[] C1;
    #else
        tstringstream SS;
        SS << setbase(Radix) << (int)I;
        assign(SS.str());
    #endif
    return *this;
}

Ztring& Ztring::From_Number (const int16u I, intu Radix)
{
    #if defined (__MINGW32__) && defined (_UNICODE) // MinGW32 does'nt support wide streams 
        wchar_t* C1=new wchar_t[33];
        _ultow (I, C1, Radix);
        assign (C1);
        delete[] C1;
    #else
        tstringstream SS;
        SS << setbase(Radix) << (unsigned int)I;
        assign(SS.str());
    #endif
    return *this;
}

Ztring& Ztring::From_Number (const int32s I, intu Radix)
{
    #if defined (__MINGW32__) && defined (_UNICODE) // MinGW32 does'nt support wide streams 
        wchar_t* C1=new wchar_t[33];
        _itow (I, C1, Radix);
        assign (C1);
        delete[] C1;
    #else
        tstringstream SS;
        SS << setbase(Radix) << I;
        assign(SS.str());
    #endif
    return *this;
}

Ztring& Ztring::From_Number (const int32u I, intu Radix)
{
    #if defined (__MINGW32__) && defined (_UNICODE) // MinGW32 does'nt support wide streams
        wchar_t* C1=new wchar_t[33];
        _ultow (I, C1, Radix);
        assign (C1);
        delete[] C1;
    #else
        tstringstream SS;
        SS << setbase(Radix) << I;
        assign(SS.str());
    #endif
    return *this;
}

#if (MAXTYPE_INT>=64)
Ztring& Ztring::From_Number (const int64s I, intu Radix)
{
    #if defined (__MINGW32__) && defined (_UNICODE) // MinGW32 does'nt support wide streams
        wchar_t* C1=new wchar_t[65];
        _i64tot (I, C1, Radix);
        assign (C1);
        delete[] C1;
    #else
        tstringstream SS;
        SS << setbase(Radix) << I;
        assign(SS.str());
    #endif
    return *this;
}

Ztring& Ztring::From_Number (const int64u I, intu Radix)
{
    #if defined (__MINGW32__) && defined (_UNICODE) // MinGW32 does'nt support wide streams
        wchar_t* C1=new wchar_t[65];
        _ui64tot (I, C1, Radix);
        assign (C1);
        delete[] C1;
    #else
        tstringstream SS;
        SS << setbase(Radix) << I;
        assign(SS.str());
    #endif
    return *this;
}
#endif

Ztring& Ztring::From_Number (const float32 F, intu Precision, ztring_t Options)
{
    Char C1[100];
    #if defined (_UNICODE)
        snwprintf (C1, 99, (Ztring(_T("%."))+Ztring::ToZtring(Precision)+_T("f")).c_str(), F);
    #else
        snprintf  (C1, 99, (Ztring(_T("%."))+Ztring::ToZtring(Precision)+_T("f")).c_str(), F);
    #endif
    assign(C1);

    if ((Options & Ztring_NoZero && size()>0) && find(_T('.'))>0)
    {
        while (size()>0 && ((*this)[size()-1]==_T('0')))
            resize(size()-1);
        if (size()>0 && (*this)[size()-1]==_T('.'))
            resize(size()-1);
    }

    return *this;
}

Ztring& Ztring::From_Number (const float64 F, intu Precision, ztring_t Options)
{
    Char C1[100];
    #if defined (_UNICODE)
         snwprintf (C1, 99, (Ztring(_T("%."))+Ztring::ToZtring(Precision)+_T("f")).c_str(), F);
    #else
        snprintf  (C1, 99, (Ztring(_T("%."))+Ztring::ToZtring(Precision)+_T("f")).c_str(), F);
    #endif
    assign(C1);

    if ((Options & Ztring_NoZero && size()>0) && find(_T('.'))>0)
    {
        while (size()>0 && ((*this)[size()-1]==_T('0')))
            resize(size()-1);
        if (size()>0 && (*this)[size()-1]==_T('.'))
            resize(size()-1);
    }

    return *this;
}

Ztring& Ztring::From_Number (const float128 F, intu Precision, ztring_t Options)
{
    Char C1[100];
    #if defined (_UNICODE)
        snwprintf (C1, 99, (Ztring(_T("%."))+Ztring::ToZtring(Precision)+_T("f")).c_str(), F);
    #else
        snprintf  (C1, 99, (Ztring(_T("%."))+Ztring::ToZtring(Precision)+_T("f")).c_str(), F);
    #endif
    assign(C1);

    if ((Options & Ztring_NoZero && size()>0) && find(_T('.'))>0)
    {
        while (size()>0 && ((*this)[size()-1]==_T('0')))
            resize(size()-1);
        if (size()>0 && (*this)[size()-1]==_T('.'))
            resize(size()-1);
    }

    return *this;
}

//---------------------------------------------------------------------------
std::wstring Ztring::To_Unicode ()
{
    #ifdef _UNICODE
        return c_str();
    #else
        return wxConvLocal.cMB2WC(c_str()).data();
    #endif
}

std::string Ztring::To_UTF8 ()
{
    #ifdef _UNICODE
        return wxConvUTF8.cWC2MB(c_str()).data();
    #else
        return wxConvUTF8.cWC2MB(wxConvLocal.cMB2WC(c_str())).data();
    #endif
}

std::string Ztring::To_Local ()
{
    #ifdef _UNICODE
        wxCharBuffer C=wxConvLocal.cWC2MB(c_str());
        if (C.data())
            return C.data();
        else
            return "";
    #else
        return c_str();
    #endif
}

//---------------------------------------------------------------------------
//Operateur ToInt
int32s Ztring::To_int32s (ztring_t Options) const
{
    //Integrity
    if (empty())
        return 0;

    //Converstion
    int32s I;
    #if defined (__MINGW32__) && defined (_UNICODE) // MinGW32 does'nt support wide streams
        I=_wtoi(c_str());
    #else
        tstringstream SS;
        SS << c_str();
        SS >> I;
    #endif

    //Rounded
    if (Options==Ztring_Rounded && find(_T("."))!=Error)
    {
        float128 F=To_float128();
        F-=I;
        if (F>=0.5)
            return I+1;
    }

    return I;
}

//---------------------------------------------------------------------------
//Operateur ToInt
int32u Ztring::To_int32u (ztring_t Options) const
{
    //Integrity
    if (empty())
        return 0;

    //Converstion
    int32u I;
    #if defined (__MINGW32__) && defined (_UNICODE) // MinGW32 does'nt support wide streams
        I=_wtoi64(c_str()); //TODO : I>0x7FFFFFFF - Replaced by i64 version to support, but not good
    #else
        tstringstream SS;
        SS << c_str();
        SS >> I;
    #endif

    //Rounded
    if (Options==Ztring_Rounded && find(_T("."))!=Error)
    {
        float128 F=To_float128();
        F-=I;
        if (F>=0.5)
            return I+1;
    }

    return I;
}

//---------------------------------------------------------------------------
//Operateur ToInt
#if (MAXTYPE_INT>=64)
int64s Ztring::To_int64s (ztring_t Options) const
{
    //Integrity
    if (empty())
        return 0;

    int64s I;
    #if defined (__MINGW32__) && defined (_UNICODE) // MinGW32 does'nt support wide streams
        I=_wtoi64(c_str());
    #else
        tstringstream SS;
        SS << c_str();
        SS >> I;
    #endif

    //Rounded
    if (Options==Ztring_Rounded && find(_T("."))!=Error)
    {
        float128 F=To_float128();
        F-=I;
        if (F>0.5)
            return I+1;
    }

    return I;
}
#endif

//---------------------------------------------------------------------------
//Operateur ToInt
#if (MAXTYPE_INT>=64)
int64u Ztring::To_int64u (ztring_t Options) const
{
    //Integrity
    if (empty())
        return 0;

    //Converstion
    int64u I;
    #if defined (__MINGW32__) && defined (_UNICODE) // MinGW32 does'nt support wide streams
        I=_wtoi64(c_str()); //TODO : I>0x7FFFFFFFFFFFFFFF
    #else
        tstringstream SS;
        SS << c_str();
        SS >> I;
    #endif

    //Rounded
    if (Options==Ztring_Rounded && find(_T("."))!=Error)
    {
        float128 F=To_float128();
        F-=I;
        if (F>=0.5)
            return I+1;
    }

    return I;
}
#endif

//---------------------------------------------------------------------------
//Operateur ToFloat
float32 Ztring::To_float32(ztring_t Options) const
{
    //Integrity
    if (empty())
        return 0;

    //Converstion
    #if defined (__MINGW32__) && defined (_UNICODE) // MinGW32 does'nt support wide streams
        return (wcstod(c_str(),NULL));
    #else //MinGW32
        float32 F;
        tstringstream SS;
        SS << c_str();
        SS >> F;

        return F;
    #endif
}

//---------------------------------------------------------------------------
//Operateur ToFloat
float64 Ztring::To_float64(ztring_t Options) const
{
    //Integrity
    if (empty())
        return 0;

    //Converstion
    #if defined (__MINGW32__) && defined (_UNICODE) // MinGW32 does'nt support wide streams
        return (wcstod(c_str(),NULL));
    #else //MinGW32
        float32 F;
        tstringstream SS;
        SS << c_str();
        SS >> F;

        return F;
    #endif
}

//---------------------------------------------------------------------------
//Operateur ToFloat
float128 Ztring::To_float128(ztring_t Options) const
{
    //Integrity
    if (empty())
        return 0;

    //Converstion
    #if defined (__MINGW32__) && defined (_UNICODE) // MinGW32 does'nt support wide streams
        return (wcstod(c_str(),NULL));
    #else //MinGW32
        float32 F;
        tstringstream SS;
        SS << c_str();
        SS >> F;

        return F;
    #endif
}

//***************************************************************************
// Edition
//***************************************************************************

//---------------------------------------------------------------------------
// Retourne une partie de la chaine
Ztring Ztring::SubString (const tstring &Begin, const tstring &End, size_type Pos, ztring_t Options) const
{
    //Recherche Début
    size_t I_Debut=find(Begin, Pos);
    if (I_Debut==Error)
        return _T("");
    I_Debut+=Begin.size();

    //gestion fin NULL
    if (End==_T(""))
        return substr(I_Debut);

    //Recherche Fin
    size_t I_Fin=find(End, I_Debut);
    if (I_Fin==Error)
    {
        if (Options & Ztring_AddLastItem)
            return substr(I_Debut);
        else
            return _T("");
    }

    return substr(I_Debut, I_Fin-I_Debut);
}

//---------------------------------------------------------------------------
//FindAndReplace
size_t Ztring::FindAndReplace (const ZenLib::tstring &ToFind, const ZenLib::tstring &ReplaceBy, size_type Pos, ZenLib::ztring_t Options)
{
   size_t Count=0;
   size_t Middle = Pos;
   while (!(Count==1 && !(Options&Ztring_Recursive)) && (Middle=find(ToFind, Middle)) != npos)
   {
      replace(Middle, ToFind.length(), ReplaceBy);
      Middle += ReplaceBy.length();
      Count++;
   }

    return Count;
}

//---------------------------------------------------------------------------
//Mise en minuscules
Ztring* Ztring::MakeLowerCase()
{
    transform(begin(), end(), begin(), (int(*)(int))tolower); //(int(*)(int)) is a patch for unix
    return this;
}

//---------------------------------------------------------------------------
// Mise en majuscules
Ztring* Ztring::MakeUpperCase()
{
    transform(begin(), end(), begin(), (int(*)(int))toupper); //(int(*)(int)) is a patch for unix
    return this;
}

//---------------------------------------------------------------------------
// Remove leading whitespaces from a string
Ztring* Ztring::TrimLeft(Char ToTrim)
{
	size_t First=0;
	while (operator[](First)!=ToTrim)
		First++;
	assign (c_str()+First);
	return this;
}

//---------------------------------------------------------------------------
// Remove trailing whitespaces from a string
Ztring* Ztring::TrimRight(Char ToTrim)
{
	size_t Last=size();
	while (operator[](Last)!=ToTrim)
		Last--;
	assign (c_str(), Last);
	return this;
}

//---------------------------------------------------------------------------
// Remove leading and trailing whitespaces from a string
Ztring* Ztring::Trim(Char ToTrim)
{
    TrimLeft(ToTrim);
    TrimRight(ToTrim);
    return this;
}

//---------------------------------------------------------------------------
// Quotes a string
Ztring* Ztring::Quote(Char ToTrim)
{
    assign(tstring(1, ToTrim)+c_str()+ToTrim);
    return this;
}

//***************************************************************************
// Information
//***************************************************************************

//---------------------------------------------------------------------------
//Count
size_t Ztring::Count (const Ztring &ToCount, ztring_t Options) const
{
    size_t Count=0;
    for (size_t Pos=0; Pos<=size(); Pos++)
        if (find(ToCount, Pos)!=npos)
        {
            Count++;
            Pos+=ToCount.size()-1; //-1 because the loop will add 1
        }
    return Count;
}

//---------------------------------------------------------------------------
//Compare
bool Ztring::Compare (const Ztring &ToCompare, const Ztring &Comparator, ztring_t Options) const
{
    //Integers management
    if (size() && ToCompare.size() && at(0)>=_T('0') && at(0)<=_T('9') && ToCompare[0]>=_T('0') && ToCompare[0]<=_T('9'))
    {
        #if (MAXTYPE_INT>=64)
            int64s Left=To_int64s();
            int64s Right=ToCompare.To_int64s();
        #else
            int32s Left=To_int32s();
            int32s Right=ToCompare.To_int32s();
        #endif
        if (Comparator==_T("==")) return (Left==Right);
        if (Comparator==_T("<"))  return (Left< Right);
        if (Comparator==_T("<=")) return (Left<=Right);
        if (Comparator==_T(">=")) return (Left>=Right);
        if (Comparator==_T(">"))  return (Left> Right);
        if (Comparator==_T("!=")) return (Left!=Right);
        if (Comparator==_T("<>")) return (Left!=Right);
        return false;
    }

    //Case sensitive option
    if (!(Options & Ztring_CaseSensitive))
    {
        //Need to copy strings and make it lowercase
        Ztring Left (c_str());
        Ztring Right (ToCompare.c_str());
        Left.MakeLowerCase();
        Right.MakeLowerCase();

        //string comparasion
        if (Comparator==_T("==")) return (Left==Right);
        if (Comparator==_T("IN")) {if (Left.find(Right)!=Error) return true; else return false;}
        if (Comparator==_T("<"))  return (Left< Right);
        if (Comparator==_T("<=")) return (Left<=Right);
        if (Comparator==_T(">=")) return (Left>=Right);
        if (Comparator==_T(">"))  return (Left> Right);
        if (Comparator==_T("!=")) return (Left!=Right);
        if (Comparator==_T("<>")) return (Left!=Right);
        return false;
    }
    else
    {
        //string comparasion
        if (Comparator==_T("==")) return (*this==ToCompare);
        if (Comparator==_T("IN")) {if (this->find(ToCompare)!=Error) return true; else return false;}
        if (Comparator==_T("<"))  return (*this< ToCompare);
        if (Comparator==_T("<=")) return (*this<=ToCompare);
        if (Comparator==_T(">=")) return (*this>=ToCompare);
        if (Comparator==_T(">"))  return (*this> ToCompare);
        if (Comparator==_T("!=")) return (*this!=ToCompare);
        if (Comparator==_T("<>")) return (*this!=ToCompare);
        return false;
    }
}

} //namespace

