//======================================================================
//-----------------------------------------------------------------------
/**
 * @file		iutest_string.hpp
 * @brief		iris unit test 文字列操作 ファイル
 *
 * @author		t.shirayanagi
 * @par			copyright
 * Copyright (C) 2011-2015, Takazumi Shirayanagi\n
 * This software is released under the new BSD License,
 * see LICENSE
*/
//-----------------------------------------------------------------------
//======================================================================
#ifndef INCG_IRIS_IUTEST_STRING_HPP_E22B02D7_E9E7_412C_B609_DC3D9C66895D_
#define INCG_IRIS_IUTEST_STRING_HPP_E22B02D7_E9E7_412C_B609_DC3D9C66895D_

//======================================================================
// include
#if defined(__MWERKS__)
#  define _MBSTATE_T
#endif

#include <wchar.h>
#include <wctype.h>
#include <stdarg.h>
#if defined(IUTEST_OS_CYGWIN)
#include <strings.h>
#endif
#if   IUTEST_HAS_STRINGSTREAM
#  include <sstream>
#elif IUTEST_HAS_STRSTREAM
#  include <strstream>
#endif
#include <string>
#include <cstring>

IUTEST_PRAGMA_CRT_SECURE_WARN_DISABLE_BEGIN()

namespace iutest {
namespace detail
{

/**
 * @internal
 * @brief	stricmp
*/
inline int iu_stricmp(const char* str1, const char* str2)
{
#if defined(IUTEST_OS_WINDOWS) && !defined(IUTEST_OS_WINDOWS_WINE)

#if defined(__BORLANDC__)
	return stricmp(str1, str2);
#else
	return _stricmp(str1, str2);
#endif

#elif !defined(__MWERKS__)
	return strcasecmp(str1, str2);

#else
	const char* l = str1;
	const char* r = str2;
	while(*l)
	{
		int ul = toupper(*l);
		int ur = toupper(*r);
		if( ul < ur ) return -1;
		if( ul > ur ) return 1;
		++l;
		++r;
	}
	if( *l < *r ) return -1;
	if( *l > *r ) return 1;
	return 0;
#endif
}

/**
 * @internal
 * @brief	wcsicmp
*/
inline int iu_wcsicmp(const wchar_t * str1, const wchar_t * str2)
{
#if   defined(_MSC_VER)
	return _wcsicmp(str1, str2);
#elif defined(IUTEST_OS_LINUX) && !defined(IUTEST_OS_LINUX_ANDROID)
	return wcscasecmp(str1, str2);
#else
	const wchar_t* l = str1;
	const wchar_t* r = str2;
	while(*l)
	{
		wchar_t ul = towupper(*l);
		wchar_t ur = towupper(*r);
		if( ul < ur ) return -1;
		if( ul > ur ) return 1;
		++l;
		++r;
	}
	if( *l < *r ) return -1;
	if( *l > *r ) return 1;
	return 0;
#endif
}

/**
 * @internal
 * @brief	snprintf
*/
inline int iu_snprintf(char* dst, size_t size, const char* format, ...)
{
	va_list va;
	va_start(va, format);
#if defined(__CYGWIN__)
#  if !defined(__STRICT_ANSI__) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || (__cplusplus >= 201103L)
	const int ret = vsnprintf(dst, size, format, va);
#  else
	IUTEST_UNUSED_VAR(size);
	const int ret = vsprintf(dst, format, va);
#  endif
#else
	const int ret = vsnprintf(dst, size, format, va);
#endif
	va_end(va);
	return ret;
}

inline bool IsEmpty(const char* p) { return p == NULL || *p == '\0'; }
inline IUTEST_CXX_CONSTEXPR bool IsSpace(char ch) { return ch == ' ' || ch =='\t'; }
inline IUTEST_CXX_CONSTEXPR const char* SkipSpace(const char* p)
{
IUTEST_PRAGMA_CONSTEXPR_CALLED_AT_RUNTIME_WARN_DISABLE_BEGIN()
	return p == NULL ? NULL : (IsSpace(*p) ? SkipSpace(++p) : p);
IUTEST_PRAGMA_CONSTEXPR_CALLED_AT_RUNTIME_WARN_DISABLE_END()
}
inline IUTEST_CXX_CONSTEXPR const char* FindComma(const char* p)
{
	return (p == NULL || *p == '\0') ? NULL : ((*p == ',') ? p : FindComma(++p));
}
inline bool IsStringEqual(const char* str1, const char* str2) { return strcmp(str1, str2) == 0; }
inline bool IsStringCaseEqual(const char* str1, const char* str2) { return iu_stricmp(str1, str2) == 0; }
inline bool IsStringForwardMatching(const char* str1, const char* str2) { return strstr(str1, str2) == str1; }

inline void StringReplace(::std::string& str, char a, const char* to)
{
	::std::string::size_type pos = 0;
	while( pos = str.find(a, pos), pos != ::std::string::npos )
	{
		str.replace(pos, 1, to);
		++pos;
	}
}
inline void StripTrailingSpace(::std::string& str)
{
	::std::string::iterator it = str.end();
	while(it != str.begin() && IsSpace(*--it))
	{
		it = str.erase(it);
	}
}
inline void StringReplaceToLF(::std::string& str)
{
	::std::string::size_type pos = 0;
	while( pos = str.find("\r\n", pos), pos != ::std::string::npos )
	{
		str.replace(pos, 2, "\n");
		++pos;
	}
	StringReplace(str, '\r', "\n");
}
inline ::std::string StringRemoveComment(const ::std::string& str)
{
	::std::string r;
	::std::string::size_type prev = 0;
	::std::string::size_type pos = str.find('\n', 0);
	while( pos != ::std::string::npos )
	{
		++pos;
		if( str[prev] != '#' ) {
			r += str.substr(prev, pos-prev);
		}
		prev = pos;
		pos = str.find('\n', pos);
	}
	if( str[prev] != '#' ) {
		r += str.substr(prev);
	}
	return r;
}

inline void StringSplit(const ::std::string& str, char delimiter, ::std::vector< ::std::string >& dst)
{
	::std::string::size_type prev = 0;
	::std::string::size_type pos = 0;
	::std::vector< ::std::string > parsed;
	while(pos = str.find(delimiter, prev), pos != ::std::string::npos)
	{
		parsed.push_back(str.substr(prev, pos - prev));
		++pos;
		prev = pos;
	}
	parsed.push_back(str.substr(prev));
	dst.swap(parsed);
}

inline IUTEST_CXX_CONSTEXPR char ToHex(unsigned int n)
{
	return (n&0xF) >= 0xA ? 'A'+((n&0xF)-0xA) : '0'+(n&0xF);
}

template<typename T>
inline ::std::string ToHexString(T value)
{
IUTEST_PRAGMA_CONSTEXPR_CALLED_AT_RUNTIME_WARN_DISABLE_BEGIN()
	const size_t kN = sizeof(T)*2;
	char buf[kN+1];
	for( size_t i=0; i < kN; ++i )
	{
		buf[i] = ToHex(static_cast<unsigned int>((value>>((kN-i-1)*4))));
	}
	buf[kN] = '\0';
	return buf;
IUTEST_PRAGMA_CONSTEXPR_CALLED_AT_RUNTIME_WARN_DISABLE_END()
}

inline ::std::string FormatIntWidth2(int value)
{
	char buf[3] = "00";
	buf[0] = (value/10)%10 + '0';
	buf[1] = (value   )%10 + '0';
	return buf;
}

inline ::std::string ShowStringQuoted(const char* str)
{
	::std::string s = "\""; s += str; s += "\"";
	return s;
}
inline ::std::string ShowStringQuoted(const ::std::string& str)
{
	::std::string s = "\""; s += str; s += "\"";
	return s;
}

//======================================================================
// declare
#if !IUTEST_HAS_STRINGSTREAM && !IUTEST_HAS_STRSTREAM
template<class _Elem, class _Traits>class iu_basic_stream;
#endif

#if   IUTEST_HAS_STRINGSTREAM

typedef ::std::stringstream stlstream;

#elif IUTEST_HAS_STRSTREAM

IUTEST_PRAGMA_MSC_WARN_PUSH()
IUTEST_PRAGMA_MSC_WARN_DISABLE(4250)
class stlstream : public ::std::strstream
{
	char buf[512];
public:
	stlstream(void)
		: ::std::strstream(buf, sizeof(buf)-2, ::std::ios::out)
	{}
	explicit stlstream(const char* str)
		: ::std::strstream(buf, sizeof(buf)-2, ::std::ios::out)
	{
		*this << str;
	}
	explicit stlstream(const ::std::string& str)
		: ::std::strstream(buf, sizeof(buf)-2, ::std::ios::out)
	{
		*this << str;
	}
public:
	::std::string str(void) const
	{
		return const_cast<stlstream*>(this)->str();
	}
	virtual ::std::string str(void)
	{
		*this << ::std::ends;
		::std::string str = ::std::strstream::str();
		return str;
	}
};

IUTEST_PRAGMA_MSC_WARN_POP()

#else
	typedef iu_basic_stream<char, ::std::char_traits<char> >		iu_stream;
	typedef iu_basic_stream<wchar_t, ::std::char_traits<wchar_t> >	iu_wstream;
#endif

//======================================================================
// class

#if !IUTEST_HAS_STRINGSTREAM && !IUTEST_HAS_STRSTREAM

template<class _Elem, class _Traits>
class iu_basic_stream
{
	typedef iu_basic_stream<_Elem, _Traits>			_Myt;
	//typedef ::std::basic_streambuf<_Elem, _Traits>	streambuf;
	//typedef ::std::basic_ostream<_Elem, _Traits>	ostream;
	typedef ::std::basic_string<_Elem, _Traits>		string;
	string s;

	template<typename T>
	struct xcs
	{
	private:
		template<typename TMP, typename TN>
		struct impl_select
		{
			template<typename TA, typename TB>
			static const TA constant(const TA a, const TB b)
			{
				(void)b;
				return a;
			}
		};
		template<typename TMP>
		struct impl_select<TMP, wchar_t>
		{
			template<typename TA, typename TB>
			static const TB constant(const TA a, const TB b)
			{
				(void)a;
				return b;
			}
		};

	public:
		typedef impl_select<void, T> select;
	};
#define IIUT_PP_XCS(txt_)	xcs<_Elem>::select::constant(txt_, L##txt_)

	struct impl
	{
		template<typename E>
		static int vastring(E* dst, const E* fmt, va_list va);
		static int vastring(char* dst, size_t len, const char* fmt, va_list va)
		{
			(void)len;
			return vsprintf(dst, fmt, va);
		}
		static int vastring(wchar_t* dst, size_t len, const wchar_t* fmt, va_list va)
		{
#ifdef IUTEST_OS_WINDOWS_MINGW
			return _vsnwprintf(dst, len, fmt, va);
#else
			return vswprintf(dst, len, fmt, va);
#endif
		}

		template<typename E>
		static int tostring(E* dst, size_t len, const E* fmt, ...)
		{
			va_list va;
			va_start(va, fmt);
			int ret = vastring(dst, len, fmt, va);
			va_end(va);
			return ret;
		}
	};
public:
	iu_basic_stream(void) {}
	explicit iu_basic_stream(const char* str) : s(str) {}
	explicit iu_basic_stream(const ::std::string& str) : s(str) {}

public:
	inline _Myt& operator<< (char v)
	{
		s += v;
		return *this;
	}
	inline _Myt& operator<< (signed char v)
	{
		s += static_cast<char>(v);
		return *this;
	}
	inline _Myt& operator<< (unsigned char v)
	{
		s += static_cast<char>(v);
		return *this;
	}
	inline _Myt& operator<< (const _Elem* v)
	{
		s += v;
		return *this;
	}
	//inline _Myt& operator<< (const signed _Elem* v)
	//{
	//	s += v;
	//	return *this;
	//}
	//inline _Myt& operator<< (const unsigned _Elem* v)
	//{
	//	s += v;
	//	return *this;
	//}
	inline _Myt& operator<< (bool v)
	{
#if 0
		_Elem a[16];
		impl::tostring(a, 16, IIUT_PP_XCS("%i"), v);
		s += a;
#else
		s += (v ? IIUT_PP_XCS("true") : IIUT_PP_XCS("false"));
#endif
		return *this;
	}
	inline _Myt& operator<< (short v)
	{
		_Elem a[64];
		impl::tostring(a, 64, IIUT_PP_XCS("%i"), v);
		s += a;
		return *this;
	}
	inline _Myt& operator<< (unsigned short v)
	{
		_Elem a[64];
		impl::tostring(a, 64, IIUT_PP_XCS("%u"), v);
		s += a;
		return *this;
	}
	inline _Myt& operator<< (int v)
	{
		_Elem a[64];
		impl::tostring(a, 64, IIUT_PP_XCS("%i"), v);
		s += a;
		return *this;
	}
	inline _Myt& operator<< (unsigned int v)
	{
		_Elem a[64];
		impl::tostring(a, 64, IIUT_PP_XCS("%u"), v);
		s += a;
		return *this;
	}
	inline _Myt& operator<< (long v)
	{
		_Elem a[64];
		impl::tostring(a, 64, IIUT_PP_XCS("%i"), v);
		s += a;
		return *this;
	}
	inline _Myt& operator<< (unsigned long v)
	{
		_Elem a[64];
		impl::tostring(a, 64, IIUT_PP_XCS("%u"), v);
		s += a;
		return *this;
	}
	inline _Myt& operator<< (long long int v)
	{
		_Elem a[64];
		impl::tostring(a, 64, IIUT_PP_XCS("%lld"), v);
		s += a;
		return *this;
	}
	inline _Myt& operator<< (unsigned long long int v)
	{
		_Elem a[64];
		impl::tostring(a, 64, IIUT_PP_XCS("%llu"), v);
		s += a;
		return *this;
	}
	inline _Myt& operator<< (float v)
	{
		_Elem a[64];
		impl::tostring(a, 64, IIUT_PP_XCS("%f"), v);
		s += a;
		return *this;
	}
	inline _Myt& operator<< (double v)
	{
		_Elem a[64];
		impl::tostring(a, 64, IIUT_PP_XCS("%l"), v);
		s += a;
		return *this;
	}
	inline _Myt& operator<< (long double v)
	{
		_Elem a[64];
		impl::tostring(a, 64, IIUT_PP_XCS("%L"), v);
		s += a;
		return *this;
	}
	inline _Myt& operator<< (const void* v)
	{
		_Elem a[64];
		impl::tostring(a, 64, IIUT_PP_XCS("%t"), v);
		s += a;
		return *this;
	}
	inline _Myt& operator<< (const ::std::string& v)
	{
		s += v;
		return *this;
	}
public:
	const string& str(void) const { return s; }
	void copyfmt(const _Myt&) {}
};

#undef IIUT_PP_XCS

#endif

IUTEST_PRAGMA_CRT_SECURE_WARN_DISABLE_END()

}	// end of namespace detail

#if IUTEST_HAS_STRINGSTREAM || IUTEST_HAS_STRSTREAM

typedef ::std::ostream iu_ostream;
typedef ::std::ostream& (*iu_basic_iomanip)(::std::ostream&);
typedef detail::stlstream iu_stringstream;

#else

typedef detail::iu_stream iu_ostream;
typedef detail::iu_stream iu_stringstream;

#endif

#if !defined(IUTEST_HAS_BIGGESTINT_OSTREAM)
#  if IUTEST_HAS_STRINGSTREAM || IUTEST_HAS_STRSTREAM
#    if (defined(_STLPORT_VERSION) && !defined(_STLP_LONG_LONG)) || (defined(_MSC_VER) && _MSC_VER < 1310)
#      define IUTEST_HAS_BIGGESTINT_OSTREAM		0
#    endif
#  endif
#endif

#if !defined(IUTEST_HAS_BIGGESTINT_OSTREAM)
#  define IUTEST_HAS_BIGGESTINT_OSTREAM			1
#endif

}	// end of namespace iutest

#endif // INCG_IRIS_IUTEST_STRING_HPP_E22B02D7_E9E7_412C_B609_DC3D9C66895D_
