/*
 * This source file is part of KEOS (Free 3D Engine)
 * For the latest info, see http://www.keosengine.org/
 * E-mails : thierry.vouriot@keosengine.org, yeri@keosengine.org
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place - Suite 330, Boston, MA 02111-1307, USA, or go to
 * http://www.gnu.org/copyleft/lesser.txt.
 *
 */
#ifndef KEOS_STRING_H
#define KEOS_STRING_H

#include "KeosPrerequisites.h"

namespace Keos 
{
    /** Utility class for manipulating Strings.  
     */
    class KEOS_EXPORT CStringUtil
    {
	public:
        typedef std::stringstream StrStreamType;

        /** Split a string.
         *  @param strString String to split.
         *  @param Tokens Destination array.
         *  @param strDelim Chars used to split the string.
         */
        static void Split(const String& strString, std::vector<String>& Tokens, const String& strDelim = " \t\n");
        
        /** Removes any whitespace characters, be it standard space or
         *  TABs and so on.
         */        
        static void Trim( String& str, bool bLeft = true, bool bRight = true );

        /** Upper-cases all the characters in the string.
         */
        static void ToLowerCase( String& str );

        /** Lower-cases all the characters in the string.
         */
        static void ToUpperCase( String& str );

        /** Lower-cases all the characters in the string.
         */
        static String ToLower(const String& strText);

        /** Upper-cases all the characters in the string.
         */
        static String ToUpper(const String& strText);

        //! Constant blank string
        static const String BLANK;
    };

    /** Utility class for string converting.
     */
    class KEOS_EXPORT CStringConverter
    {
    public:

        /** Converts a Real to a String. 
         */
        static String ToString(float fVal, ushort nPrecision = 6, 
            ushort nWidth = 0, char fill = ' ', 
            std::ios::fmtflags flags = std::ios::fmtflags(0) );

		/** Converts an int to a String. 
		 */
        static String ToString(int nVal, unsigned short nWidth = 0, 
            char fill = ' ', 
            std::ios::fmtflags flags = std::ios::fmtflags(0) );

        /** Converts a boolean to a String. 
         *  @param bVal Value to be converted.
         *  @param bYesNo If set to true, result is 'yes' or 'no' instead of 'true' or 'false'.
         */
        static String ToString(bool bVal, bool bYesNo = false);
        
        /** Converts a String to a Real. 
         *  @returns 0.0 if the value could not be parsed, otherwise the Real version of the String.
         */
        static Real ParseReal(const String& strVal);
        
        /** Converts a String to a whole number. 
         *  @returns 0.0 if the value could not be parsed, otherwise the numeric version of the String.
         */
        static int ParseInt(const String& strVal);
        
        /** Converts a String to a whole number. 
         *  @returns 0.0 if the value could not be parsed, otherwise the numeric version of the String.
         */
        static uint ParseUnsignedInt(const String& strVal);
        
        /** Converts a String to a whole number. 
         *  @returns 0.0 if the value could not be parsed, otherwise the numeric version of the String.
         */
        static long ParseLong(const String& strVal);
        
        /** Converts a String to a whole number. 
         *  @returns 0.0 if the value could not be parsed, otherwise the numeric version of the String.
         */
        static ulong ParseUnsignedLong(const String& strVal);
        
        /** Converts a String to a boolean. 
         *  @param strVal Accepts 'true' or 'false' as input.
         */
        static bool ParseBool(const String& strVal);
    };

    class CStringBuilder
    {
    public :

        //----------------------------------------------------------------
        // Constructeur par défaut
        //----------------------------------------------------------------
        CStringBuilder();

        //----------------------------------------------------------------
        // Construit l'injecteur avec une première valeur
        //----------------------------------------------------------------
        template <typename T> CStringBuilder(const T& Value);

        //----------------------------------------------------------------
        // Injecte une valeur dans la chaîne
        //----------------------------------------------------------------
        template <typename T> CStringBuilder& operator ()(const T& Value);

        //----------------------------------------------------------------
        // Opérateur de conversion implicite en string
        //----------------------------------------------------------------
        operator std::string();

    private :

        //----------------------------------------------------------------
        // Données membres
        //----------------------------------------------------------------
        std::ostringstream m_OutStream; ///< Flux-chaîne utilisé pourl'injection des valeurs
    };


    ////////////////////////////////////////////////////////////
    /// Template servant à construire les classes singleton
    ////////////////////////////////////////////////////////////
    class CStringExtractor
    {
    public :

        //----------------------------------------------------------------
        // Construit l'extracteur à partir d'une chaîne
        //----------------------------------------------------------------
        CStringExtractor(const std::string& Text);

        //----------------------------------------------------------------
        // Extrait une valeur de type T
        //----------------------------------------------------------------
        template <typename T> CStringExtractor& operator ()(T& Value);

        //----------------------------------------------------------------
        // Vérifie qu'il n'y a plus rien à extraire
        //----------------------------------------------------------------
        void ThrowIfEOF();

    private :

        //----------------------------------------------------------------
        // Données membres
        //----------------------------------------------------------------
        std::istringstream m_InStream; ///< Flux-chaîne utilisé pour l'extraction des valeurs
    };

    ////////////////////////////////////////////////////////////
/// Construit l'injecteur avec une première valeur
///
/// \param Value : Valeur à injecter
///
////////////////////////////////////////////////////////////
template <typename T>
inline CStringBuilder::CStringBuilder(const T& Value)
{
    m_OutStream << Value;
}


////////////////////////////////////////////////////////////
/// Injecte une valeur dans la chaîne
///
/// \param Value : Valeur à injecter
///
/// \return Référence sur l'instance de l'injecteur
///
////////////////////////////////////////////////////////////
template <typename T>
inline CStringBuilder& CStringBuilder::operator ()(const T& Value)
{
    m_OutStream << Value;

    return *this;
}


////////////////////////////////////////////////////////////
/// Opérateur de conversion implicite en string
///
/// \return Chaîne de caractère construite avec les valeurs injectées
///
////////////////////////////////////////////////////////////
inline CStringBuilder::operator std::string()
{
    return m_OutStream.str();
}


////////////////////////////////////////////////////////////
/// Construit l'extracteur à partir d'une chaîne
///
/// \param Text : Texte à convertir
///
////////////////////////////////////////////////////////////
inline CStringExtractor::CStringExtractor(const std::string& Text) :
m_InStream(Text)
{

}


////////////////////////////////////////////////////////////
/// Extrait une valeur de type T
///
/// \param Value : Valeur à extraire
///
/// \return Référence sur l'extracteur
///
////////////////////////////////////////////////////////////
template <typename T>
inline CStringExtractor& CStringExtractor::operator ()(T& Value)
{
    if (!(m_InStream >> Value))
    {
        if (m_InStream.eof())
            throw CBadConversion(CStringBuilder("Tentative d'extraire un \"")(typeid(T).name())("\" à partir d'une chaîne vide"),"");
        else
            throw CBadConversion(CStringBuilder("Impossible de convertir de \"string\" en \"")(typeid(T).name())("\""),"");
    }

    return *this;
}


////////////////////////////////////////////////////////////
/// Vérifie qu'il n'y a plus rien à extraire
///
////////////////////////////////////////////////////////////
inline void CStringExtractor::ThrowIfEOF()
{
    std::string Left;
    if (std::getline(m_InStream, Left))
        throw CBadConversion("Chaîne trop longue, \"" + Left + "\" n'a pas été extrait","");
}


} // namespace Keos

#endif // KEOS_STRING_H

