// **************************************************************************
// * This file is part of the zen::Xml project. It is distributed under the *
// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt           *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved        *
// **************************************************************************

#ifndef ZEN_XML_CONVERT_TEXT_HEADER_018727339083427097434
#define ZEN_XML_CONVERT_TEXT_HEADER_018727339083427097434

#include <zen/utf.h>
#include <zen/string_tools.h>

namespace zen
{
/**
\file
\brief Handle conversion of string-convertible types to and from std::string.

It is \b not required to call these functions directly. They are implicitly used by zen::XmlElement::getValue(),
zen::XmlElement::setValue(), zen::XmlElement::getAttribute() and zen::XmlElement::setAttribute().
\n\n
Conversions for the following user types are supported by default:
	- strings - std::string, std::wstring, char*, wchar_t*, char, wchar_t, ect..., all STL-compatible-string-classes
	- numbers - int, double, float, bool, long, ect..., all built-in numbers
	- STL containers - std::map, std::set, std::vector, std::list, ect..., all STL-compatible-containers
	- std::pair

You can add support for additional types via template specialization. \n\n
Specialize zen::readStruc() and zen::writeStruc() to enable conversion from structured user types to XML elements.
Specialize zen::readText() and zen::writeText() to enable conversion from string-convertible user types to std::string.
Prefer latter if possible since it does not only enable conversions from XML elements to user data, but also from and to XML attributes.
\n\n
<b> Example: </b> type "bool"
\code
namespace zen
{
template <> inline
void writeText(const bool& value, std::string& output)
{
    output = value ? "true" : "false";
}

template <> inline
bool readText(const std::string& input, bool& value)
{
    std::string tmp = input;
    zen::trim(tmp);
    if (tmp == "true")
        value = true;
    else if (tmp == "false")
        value = false;
    else
        return false;
    return true;
}
}
\endcode
*/


///Convert text to user data - used by XML elements and attributes
/**
  \param input Input text.
  \param value Conversion target value.
  \return "true" if value was read successfully.
*/
template <class T> bool readText(const std::string& input, T& value);
///Convert user data into text - used by XML elements and attributes
/**
  \param value The value to be converted.
  \param output Output text.
*/
template <class T> void writeText(const T& value, std::string& output);


/* Different classes of data types:

-----------------------------
| structured                |  readStruc/writeStruc - e.g. string-convertible types, STL containers, std::pair, structured user types
| ------------------------- |
| | to-string-convertible | |  readText/writeText   - e.g. string-like types, all built-in arithmetic numbers, bool
| | ---------------       | |
| | | string-like |       | |  utfCvrtTo            - e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
| | ---------------       | |
| ------------------------- |
-----------------------------
*/















//------------------------------ implementation -------------------------------------

//Conversion from arbitrary types to text (for use with XML elements and attributes)
enum TextType
{
    TEXT_TYPE_BOOL,
    TEXT_TYPE_NUMBER,
    TEXT_TYPE_STRING,
    TEXT_TYPE_OTHER,
};

template <class T>
struct GetTextType : StaticEnum<TextType,
        IsSameType<T, bool>::value ? TEXT_TYPE_BOOL   :
        IsStringLike<T>::value     ? TEXT_TYPE_STRING : //string before number to correctly handle char/wchar_t -> this was an issue with Loki only!
        IsArithmetic<T>::value     ? TEXT_TYPE_NUMBER : //
        TEXT_TYPE_OTHER> {};

//######################################################################################

template <class T, TextType type>
struct ConvertText;
/* -> expected interface
{
	void writeText(const T& value, std::string& output) const;
	bool readText(const std::string& input, T& value) const;
};
*/

//partial specialization: type bool
template <class T>
struct ConvertText<T, TEXT_TYPE_BOOL>
{
    void writeText(bool value, std::string& output) const
    {
        output = value ? "true" : "false";
    }
    bool readText(const std::string& input, bool& value) const
    {
        std::string tmp = input;
        zen::trim(tmp);
        if (tmp == "true")
            value = true;
        else if (tmp == "false")
            value = false;
        else
            return false;
        return true;
    }
};

//partial specialization: handle conversion for all built-in arithmetic types!
template <class T>
struct ConvertText<T, TEXT_TYPE_NUMBER>
{
    void writeText(const T& value, std::string& output) const
    {
        output = numberTo<std::string>(value);
    }
    bool readText(const std::string& input, T& value) const
    {
        value = stringTo<T>(input);
        return true;
    }
};

//partial specialization: handle conversion for all string-like types!
template <class T>
struct ConvertText<T, TEXT_TYPE_STRING>
{
    void writeText(const T& value, std::string& output) const
    {
        output = utfCvrtTo<std::string>(value);
    }
    bool readText(const std::string& input, T& value) const
    {
        value = utfCvrtTo<T>(input);
        return true;
    }
};


//partial specialization: unknown type
template <class T>
struct ConvertText<T, TEXT_TYPE_OTHER>
{
    //###########################################################################################################################################
    assert_static(sizeof(T) == -1);
    /*
        ATTENTION: The data type T is yet unknown to the zen::Xml framework!

        Please provide a specialization for T of the following two functions in order to handle conversions to XML elements and attributes

    	template <> void zen::writeText(const T& value, std::string& output)
        template <> bool zen::readText(const std::string& input, T& value)

    	If T is structured and cannot be converted to a text representation specialize these two functions to allow at least for conversions to XML elements:

    	template <> void zen::writeStruc(const T& value, XmlElement& output)
        template <> bool zen::readStruc(const XmlElement& input, T& value)
    */
    //###########################################################################################################################################
};


template <class T> inline
void writeText(const T& value, std::string& output)
{
    ConvertText<T, GetTextType<T>::value>().writeText(value, output);
}


template <class T> inline
bool readText(const std::string& input, T& value)
{
    return ConvertText<T, GetTextType<T>::value>().readText(input, value);
}
}

#endif //ZEN_XML_CONVERT_TEXT_HEADER_018727339083427097434