/*
**  ClanLib SDK
**  Copyright (c) 1997-2020 The ClanLib Team
**
**  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.
**
**  Note: Some of the libraries ClanLib may link to may have additional
**  requirements or restrictions.
**
**  File Author(s):
**
**    Magnus Norddahl
*/

#include "Core/precomp.h"
#include "API/Core/Text/string_help.h"
#include "API/Core/Text/logger.h"
#include "API/Core/Text/utf8_reader.h"
#include "API/Core/System/exception.h"
#include "API/Core/System/databuffer.h"
#ifndef WIN32
#include <wchar.h>
#include <wctype.h>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <string>
#else
#include <cstring>
#if defined UNICODE && !defined _UNICODE
#define _UNICODE
#endif
#include <tchar.h>
#endif

#include <sstream>

#ifdef __MINGW32__
#include <cstdio>
#endif

// This function or variable may be unsafe. Consider using xxxx instead.
// To disable deprecation, use _CRT_SECURE_NO_DEPRECATE. See online help for details.
#ifdef WIN32
#pragma warning(disable: 4996)
#endif
// We disable this warning because the usage patterns we use here are safe,
// and although we could switch to newer more 'safe' versions, this may break
// compilation in earlier versions of Visual Studio. -- mbn 13. sep 2006

namespace clan
{
	#ifndef WIN32
	#ifndef HAVE_WCSCASECMP
		int	cl_wcscasecmp(const wchar_t *, const wchar_t *);
	#define wcscasecmp cl_wcscasecmp
	#endif
	#endif
    
	std::vector<std::string> StringHelp::split_text(
		const std::string &text,
		const std::string &split_string,
		bool skip_empty)
	{
		std::vector<std::string> result;
		std::string::size_type end_pos = 0, begin_pos = 0;
		while (true)
		{
			end_pos = text.find(split_string, begin_pos);
			if (end_pos == std::string::npos)
			{
				if (begin_pos != text.length())
					result.push_back(text.substr(begin_pos));
				break;
			}
			else
			{
				if (!skip_empty || begin_pos != end_pos)
					result.push_back(text.substr(begin_pos, end_pos-begin_pos));
				begin_pos = end_pos + split_string.length();
			}
		}
		return result;
	}

	std::string StringHelp::trim(const std::string &text)
	{
		std::string::size_type first_char = text.find_first_not_of(" \r\n\t");
		std::string::size_type last_char = text.find_last_not_of(" \r\n\t");
		if (first_char == std::string::npos)
			return std::string();
		if (last_char == std::string::npos)
			return std::string();
		return text.substr(first_char, last_char - first_char + 1);
	}

	int StringHelp::compare(const std::string &a, const std::string &b, bool case_insensitive)
	{
	#ifdef WIN32
		if (case_insensitive)
			return stricmp(a.c_str(), b.c_str());
		else
			return strcmp(a.c_str(), b.c_str());
	#else
		if (case_insensitive)
			return strcasecmp(a.c_str(), b.c_str());
		else
			return strcmp(a.c_str(), b.c_str());
	#endif
	}

	int StringHelp::compare(const std::wstring &a, const std::wstring &b, bool case_insensitive)
	{
	#ifdef WIN32
		if (case_insensitive)
			return wcsicmp(a.c_str(), b.c_str());
		else
			return wcscmp(a.c_str(), b.c_str());
	#else
		if (case_insensitive)
	#ifdef __sun
			return std::wcscasecmp(a.c_str(), b.c_str());
	#else
			return wcscasecmp(a.c_str(), b.c_str());
	#endif
		else
			return wcscmp(a.c_str(), b.c_str());
	#endif
	}

	std::string StringHelp::text_to_upper(const std::string &s)
	{
		return local8_to_upper(s);
	}
	
	std::string StringHelp::wchar_to_utf8(wchar_t value)
	{
		return unicode_to_utf8(value);
	}

	std::string StringHelp::unicode_to_utf8(unsigned int value)
	{
		char text[8];

		if ( (value < 0x80) && (value > 0) )
		{
			text[0] = (char) value;
			text[1] = 0;
		}
		else if( value < 0x800 )
		{
			text[0] = (char) (  0xc0 | ( value >> 6 ));
			text[1] = (char) (  0x80 | ( value & 0x3f ));
			text[2] = 0;
		}
		else if( value < 0x10000 )
		{
			text[0] = (char) (  0xe0 | ( value >> 12 ));
			text[1] = (char) (  0x80 | ( (value >> 6) & 0x3f ));
			text[2] = (char) (  0x80 | ( value & 0x3f ));
			text[3] = 0;
	
		}
		else if( value < 0x200000 )
		{
			text[0] = (char) (  0xf0 | ( value >> 18 ));
			text[1] = (char) (  0x80 | ( (value >> 12) & 0x3f ));
			text[2] = (char) (  0x80 | ( (value >> 6) & 0x3f ));
			text[3] = (char) (  0x80 | ( value & 0x3f ));
			text[4] = 0;

		}
		else if( value < 0x4000000 )
		{
			text[0] = (char) (  0xf8 | ( value >> 24 ));
			text[1] = (char) (  0x80 | ( (value >> 18) & 0x3f ));
			text[2] = (char) (  0x80 | ( (value >> 12) & 0x3f ));
			text[3] = (char) (  0x80 | ( (value >> 6) & 0x3f ));
			text[4] = (char) (  0x80 | ( value & 0x3f ));
			text[5] = 0;

		}
		else if( value < 0x80000000 )
		{
			text[0] = (char) (  0xfc | ( value >> 30 ));
			text[1] = (char) (  0x80 | ( (value >> 24) & 0x3f ));
			text[2] = (char) (  0x80 | ( (value >> 18) & 0x3f ));
			text[3] = (char) (  0x80 | ( (value >> 12) & 0x3f ));
			text[4] = (char) (  0x80 | ( (value >> 6) & 0x3f ));
			text[5] = (char) (  0x80 | ( value & 0x3f ));
			text[6] = 0;
		}
		else
		{
			text[0] = 0;	// Invalid wchar value
		}
		return text;
	}

	std::string StringHelp::local8_to_upper(const std::string &s)
	{
		std::string result = s;
		std::string::size_type index, size;
		size = result.length();
		for (index = 0; index < size; index++)
		{
			result[index] = (unsigned char) toupper((unsigned char) result[index]);
		}
		return result;
	}

	std::wstring StringHelp::ucs2_to_upper(const std::wstring &s)
	{
		std::wstring result = s;
		std::wstring::size_type index, size;
		size = result.length();
		for (index = 0; index < size; index++)
		{
			result[index] = towupper(result[index]);
		}
		return result;
	}
	
	std::string StringHelp::text_to_lower(const std::string &s)
	{
		return local8_to_lower(s);
	}
	
	std::string StringHelp::local8_to_lower(const std::string &s)
	{
		std::string result = s;
		std::string::size_type index, size;
		size = result.length();
		for (index = 0; index < size; index++)
		{
			result[index] = (unsigned char) tolower((unsigned char) result[index]);
		}
		return result;
	}
	
	std::wstring StringHelp::ucs2_to_lower(const std::wstring &s)
	{
		std::wstring result = s;
		std::wstring::size_type index, size;
		size = result.length();
		for (index = 0; index < size; index++)
		{
			result[index] = towlower(result[index]);
		}
		return result;
	}

	std::string StringHelp::remove_trailing_zeros(std::string text)
	{
		if (text.find_first_of('.') != std::string::npos)
		{
			while (!text.empty() && text.back() == '0') text.pop_back();
			if (!text.empty() && text.back() == '.') text.pop_back();
		}
		return text;
	}

	std::wstring StringHelp::remove_trailing_zeros(std::wstring text)
	{
		if (text.find_first_of(L'.') != std::wstring::npos)
		{
			while (!text.empty() && text.back() == L'0') text.pop_back();
			if (!text.empty() && text.back() == L'.') text.pop_back();
		}
		return text;
	}

	std::string StringHelp::float_to_text(float value, int num_decimal_places, bool remove_zeros)
	{
		return float_to_local8(value, num_decimal_places, remove_zeros);
	}

	std::string StringHelp::float_to_local8(float value, int num_decimals, bool remove_zeros)
	{
		char buf[64];
		memset(buf, 0, 64);
	#ifdef WIN32
		_snprintf(buf, 63, ("%." + StringHelp::int_to_local8(num_decimals) + "f").c_str(), value);
	#else
		snprintf(buf, 63, ("%." + StringHelp::int_to_local8(num_decimals) + "f").c_str(), value);
	#endif
		if (remove_zeros)
			return remove_trailing_zeros(std::string(buf));
		else
			return std::string(buf);
	}
	
	std::wstring StringHelp::float_to_ucs2(float value, int num_decimals, bool remove_zeros)
	{
	#ifdef WIN32
		WCHAR buf[64];
		memset(buf, 0, 64 * sizeof(WCHAR));
		swprintf(buf, (L"%." + StringHelp::int_to_ucs2(num_decimals) + L"f").c_str(), value);
		if (remove_zeros)
			return remove_trailing_zeros(std::wstring(buf));
		else
			return std::wstring(buf);
	#else
		wchar_t buf[64];
		memset(buf, 0, 64 * sizeof(wchar_t));
		swprintf(buf, 63, (L"%." + StringHelp::int_to_ucs2(num_decimals) + L"f").c_str(), value);
		if (remove_zeros)
			return remove_trailing_zeros(std::wstring(buf));
		else
			return std::wstring(buf);
	#endif
	}

	float StringHelp::text_to_float(const std::string &value)
	{
		return local8_to_float(value);
	}
	
	float StringHelp::local8_to_float(const std::string &value)
	{
		float result = 0.0;
		sscanf(value.c_str(), "%f", &result);
		return result;
	}
	
	float StringHelp::ucs2_to_float(const std::wstring &value)
	{
		float result = 0.0;
		swscanf(value.c_str(), L"%f", &result);
		return result;
	}

	std::string StringHelp::double_to_text(double value, int num_decimals)
	{
		return double_to_local8(value, num_decimals);
	}

	std::string StringHelp::double_to_local8(double value, int num_decimals)
	{
		char buf[64];
		memset(buf, 0, 64);
	#ifdef WIN32
		_snprintf(buf, 63, ("%." + StringHelp::int_to_local8(num_decimals) + "f").c_str(), value);
	#else
		snprintf(buf, 63, ("%." + StringHelp::int_to_local8(num_decimals) + "f").c_str(), value);
	#endif
		return std::string(buf);
	}
	
	std::wstring StringHelp::double_to_ucs2(double value, int num_decimals)
	{
	#ifdef WIN32
		WCHAR buf[64];
		memset(buf, 0, 64 * sizeof(WCHAR));
		swprintf(buf, (L"%." + StringHelp::int_to_ucs2(num_decimals) + L"f").c_str(), value);
		return std::wstring(buf);
	#else
		wchar_t buf[64];
		memset(buf, 0, 64 * sizeof(wchar_t));
		swprintf(buf, 63, (L"%." + StringHelp::int_to_ucs2(num_decimals) + L"f").c_str(), value);
		return std::wstring(buf);
	#endif
	}
	
	double StringHelp::text_to_double(const std::string &value)
	{
		return local8_to_double(value);
	}
	
	double StringHelp::local8_to_double(const std::string &value)
	{
		double result = 0.0;
		sscanf(value.c_str(), "%lf", &result);
		return result;
	}
	
	double StringHelp::ucs2_to_double(const std::wstring &value)
	{
		double result = 0.0;
		swscanf(value.c_str(), L"%lf", &result);
		return result;
	}

	std::string StringHelp::int_to_text(int value)
	{
		return int_to_local8(value);
	}

	std::string StringHelp::int_to_local8(int value)
	{
		char buf[32];
		memset(buf, 0, 32);
	#ifdef WIN32
		_snprintf(buf, 31, "%d", value);
	#else
		snprintf(buf, 31, "%d", value);
	#endif
		return std::string(buf);
	}
	
	std::wstring StringHelp::int_to_ucs2(int value)
	{
	#ifdef WIN32
		WCHAR buf[32];
		memset(buf, 0, 32 * sizeof(WCHAR));
		swprintf(buf, L"%d", value);
		return std::wstring(buf);
	#else
		wchar_t buf[32];
		memset(buf, 0, 32 * sizeof(wchar_t));
		swprintf(buf, 31, L"%d", value);
		return std::wstring(buf);
	#endif
	}

	int StringHelp::text_to_int(const std::string &value, int base)
	{
		return local8_to_int(value, base);
	}
	
	int StringHelp::local8_to_int(const std::string &value, int base)
	{
		if (base == 10)
		{
			return atoi(value.c_str());
		}
		else if (base == 16)
		{
			int result = 0;
			std::string::size_type i, length;
			length = value.length();
			for (i = 0; i < length; i++)
			{
				if (value[i] >= '0' && value[i] <= '9')
				{
					result = (result << 4) + (value[i] - '0');
				}
				else if (value[i] >= 'a' && value[i] <= 'f')
				{
					result = (result << 4) + 10 + (value[i] - 'a');
				}
				else if (value[i] >= 'A' && value[i] <= 'F')
				{
					result = (result << 4) + 10 + (value[i] - 'A');
				}
				else
				{
					break;
				}
			}
			return result;
		}
		else
		{
			throw Exception("Unsupported base passed for local8_to_int");
		}
	}
	
	int StringHelp::ucs2_to_int(const std::wstring &value, int base)
	{
		if (base == 10)
		{
	#ifdef WIN32
			return _wtoi(value.c_str());
	#else
			std::wistringstream stream(value.c_str());
			int num;
			stream >> num;
			return num;
	#endif
		}
		else if (base == 16)
		{
			int result = 0;
			std::wstring::size_type i, length;
			length = value.length();
			for (i = 0; i < length; i++)
			{
				if (value[i] >= L'0' && value[i] <= L'9')
				{
					result = (result << 4) + (value[i] - L'0');
				}
				else if (value[i] >= L'a' && value[i] <= L'f')
				{
					result = (result << 4) + 10 + (value[i] - L'a');
				}
				else if (value[i] >= L'A' && value[i] <= L'F')
				{
					result = (result << 4) + 10 + (value[i] - L'A');
				}
				else
				{
					break;
				}
			}
			return result;
		}
		else
		{
			throw Exception("Unsupported base passed for ucs2_to_int");
		}
	}

	std::string StringHelp::uint_to_text(unsigned int value)
	{
		return uint_to_local8(value);
	}

	std::string StringHelp::uint_to_local8(unsigned int value)
	{
		char buf[32];
		memset(buf, 0, 32);
	#ifdef WIN32
		_snprintf(buf, 31, "%u", value);
	#else
		snprintf(buf, 31, "%u", value);
	#endif
		return std::string(buf);
	}
	
	std::wstring StringHelp::uint_to_ucs2(unsigned int value)
	{
	#ifdef WIN32
		WCHAR buf[32];
		memset(buf, 0, 32 * sizeof(WCHAR));
		swprintf(buf, L"%u", value);
		return std::wstring(buf);
	#else
		wchar_t buf[32];
		memset(buf, 0, 32 * sizeof(wchar_t));
		swprintf(buf, 31, L"%u", value);
		return std::wstring(buf);
	#endif
	}

	std::string StringHelp::ull_to_text(unsigned long long value)
	{
		return uint_to_local8(value);
	}

	std::string StringHelp::ull_to_local8(unsigned long long value)
	{
		std::ostringstream stream;
		stream << value;
	
		return std::string(stream.str().c_str());
	}
	
	std::wstring StringHelp::ull_to_ucs2(unsigned long long value)
	{
		std::wostringstream stream;
		stream << value;
	
		return std::wstring(stream.str().c_str());
	}

	std::string StringHelp::ll_to_text(long long value)
	{
		return uint_to_local8(value);
	}

	std::string StringHelp::ll_to_local8(long long value)
	{
		std::ostringstream stream;
		stream << value;
	
		return std::string(stream.str().c_str());
	}
	
	std::wstring StringHelp::ll_to_ucs2(long long value)
	{
		std::wostringstream stream;
		stream << value;
	
		return std::wstring(stream.str().c_str());
	}

	unsigned long long StringHelp::text_to_ull(const std::string &value, int base)
	{
		return local8_to_ull(value, base);
	}

	unsigned long long StringHelp::local8_to_ull(const std::string &value, int base)
	{
		if (base == 10)
		{
			std::istringstream stream(value.c_str());
			unsigned long long num;
			stream >> num;
			return num;
		}
		else if (base == 16)
		{
			unsigned long long result = 0;
			std::string::size_type i, length;
			length = value.length();
			for (i = 0; i < length; i++)
			{
				if (value[i] >= '0' && value[i] <= '9')
				{
					result = (result << 4) + (value[i] - '0');
				}
				else if (value[i] >= 'a' && value[i] <= 'f')
				{
					result = (result << 4) + 10 + (value[i] - 'a');
				}
				else if (value[i] >= 'A' && value[i] <= 'F')
				{
					result = (result << 4) + 10 + (value[i] - 'A');
				}
				else
				{
					break;
				}
			}
			return result;
		}
		else
		{
			throw Exception("Unsupported base passed for local8_to_ull");
		}
	}
	
	unsigned long long StringHelp::ucs2_to_ull(const std::wstring &value, int base)
	{
		if (base == 10)
		{
			std::wistringstream stream(value.c_str());
			unsigned long long num;
			stream >> num;
			return num;
		}
		else if (base == 16)
		{
			unsigned long long result = 0;
			std::wstring::size_type i, length;
			length = value.length();
			for (i = 0; i < length; i++)
			{
				if (value[i] >= L'0' && value[i] <= L'9')
				{
					result = (result << 4) + (value[i] - L'0');
				}
				else if (value[i] >= L'a' && value[i] <= L'f')
				{
					result = (result << 4) + 10 + (value[i] - L'a');
				}
				else if (value[i] >= L'A' && value[i] <= L'F')
				{
					result = (result << 4) + 10 + (value[i] - L'A');
				}
				else
				{
					break;
				}
			}
			return result;
		}
		else
		{
			throw Exception("Unsupported base passed for ucs2_to_ull");
		}
	}

	long long StringHelp::text_to_ll(const std::string &value, int base)
	{
		return local8_to_ll(value, base);
	}

	long long StringHelp::local8_to_ll(const std::string &value, int base)
	{
		if (base == 10)
		{
			std::istringstream stream(value.c_str());
			long long num;
			stream >> num;
			return num;
		}
		else if (base == 16)
		{
			long long result = 0;
			std::string::size_type i, length;
			length = value.length();
			for (i = 0; i < length; i++)
			{
				if (value[i] >= '0' && value[i] <= '9')
				{
					result = (result << 4) + (value[i] - '0');
				}
				else if (value[i] >= 'a' && value[i] <= 'f')
				{
					result = (result << 4) + 10 + (value[i] - 'a');
				}
				else if (value[i] >= 'A' && value[i] <= 'F')
				{
					result = (result << 4) + 10 + (value[i] - 'A');
				}
				else
				{
					break;
				}
			}
			return result;
		}
		else
		{
			throw Exception("Unsupported base passed for local8_to_ll");
		}
	}
	
	long long StringHelp::ucs2_to_ll(const std::wstring &value, int base)
	{
		if (base == 10)
		{
			std::wistringstream stream(value.c_str());
			long long num;
			stream >> num;
			return num;
		}
		else if (base == 16)
		{
			long long result = 0;
			std::wstring::size_type i, length;
			length = value.length();
			for (i = 0; i < length; i++)
			{
				if (value[i] >= L'0' && value[i] <= L'9')
				{
					result = (result << 4) + (value[i] - L'0');
				}
				else if (value[i] >= L'a' && value[i] <= L'f')
				{
					result = (result << 4) + 10 + (value[i] - L'a');
				}
				else if (value[i] >= L'A' && value[i] <= L'F')
				{
					result = (result << 4) + 10 + (value[i] - L'A');
				}
				else
				{
					break;
				}
			}
			return result;
		}
		else
		{
			throw Exception("Unsupported base passed for ucs2_to_ll");
		}
	}

	unsigned int StringHelp::text_to_uint(const std::string &value, int base)
	{
		return local8_to_uint(value, base);
	}
	
	unsigned int StringHelp::local8_to_uint(const std::string &value, int base)
	{
		if (base == 10)
		{
			return (unsigned int) atoi(value.c_str());
		}
		else if (base == 16)
		{
			unsigned int result = 0;
			std::string::size_type i, length;
			length = value.length();
			for (i = 0; i < length; i++)
			{
				if (value[i] >= '0' && value[i] <= '9')
				{
					result = (result << 4) + (value[i] - '0');
				}
				else if (value[i] >= 'a' && value[i] <= 'f')
				{
					result = (result << 4) + 10 + (value[i] - 'a');
				}
				else if (value[i] >= 'A' && value[i] <= 'F')
				{
					result = (result << 4) + 10 + (value[i] - 'A');
				}
				else
				{
					break;
				}
			}
			return result;
		}
		else
		{
			throw Exception("Unsupported base passed for local8_to_uint");
		}
	}
	
	unsigned int StringHelp::ucs2_to_uint(const std::wstring &value, int base)
	{
		if (base == 10)
		{
	#ifdef WIN32
			return (unsigned int) _wtoi(value.c_str());
	#else
			std::wistringstream stream(value.c_str());
			unsigned int num;
			stream >> num;
			return num;
	#endif
		}
		else if (base == 16)
		{
			unsigned int result = 0;
			std::wstring::size_type i, length;
			length = value.length();
			for (i = 0; i < length; i++)
			{
				if (value[i] >= L'0' && value[i] <= L'9')
				{
					result = (result << 4) + (value[i] - L'0');
				}
				else if (value[i] >= L'a' && value[i] <= L'f')
				{
					result = (result << 4) + 10 + (value[i] - L'a');
				}
				else if (value[i] >= L'A' && value[i] <= L'F')
				{
					result = (result << 4) + 10 + (value[i] - L'A');
				}
				else
				{
					break;
				}
			}
			return result;
		}
		else
		{
			throw Exception("Unsupported base passed for ucs2_to_uint");
		}
	}

	std::string StringHelp::bool_to_text(bool value)
	{
		return bool_to_local8(value);
	}

	std::string StringHelp::bool_to_local8(bool value)
	{
		return value ? "true" : "false";
	}

	std::wstring StringHelp::bool_to_ucs2(bool value)
	{
		return value ? L"true" : L"false";
	}

	bool StringHelp::text_to_bool(const std::string &value)
	{
		return local8_to_bool(value);
	}

	bool StringHelp::local8_to_bool(const std::string &value)
	{
		if (StringHelp::compare(value, "true", true) == 0)
			return true;
		else if (StringHelp::compare(value, "yes", true) == 0)
			return true;
		else if (value == "1")
			return true;
		else
			return false;
	}

	bool StringHelp::ucs2_to_bool(const std::wstring &value)
	{
		if (StringHelp::compare(value, L"true", true) == 0)
			return true;
		else if (StringHelp::compare(value, L"yes", true) == 0)
			return true;
		else if (value == L"1")
			return true;
		else
			return false;
	}

	std::string StringHelp::ucs2_to_latin1(const std::wstring &ucs2)
	{
		std::string::size_type i, length = ucs2.length();
		std::string latin1(length, ' ');
		for (i=0; i<length; i++)
			latin1[i] = (char) ucs2[i];
		return latin1;
	}

	std::string StringHelp::ucs2_to_latin9(const std::wstring &ucs2)
	{
		std::string::size_type i, length = ucs2.length();
		std::string latin1(length, ' ');
		for (i=0; i<length; i++)
			latin1[i] = (ucs2[i] != 0x20ac) ? ucs2[i] : 0xa4;
		return latin1;
	}

	std::string StringHelp::ucs2_to_local8(const std::wstring &ucs2)
	{
		return ucs2_to_latin9(ucs2);
	}

	std::string StringHelp::ucs2_to_utf8(const std::wstring &ucs2)
	{
		// Calculate length:

		std::wstring::size_type length_ucs2 = ucs2.length();
		std::string::size_type length_utf8 = 0;
		std::wstring::size_type pos;
		for (pos = 0; pos < length_ucs2; pos++)
		{
			if (ucs2[pos] < 0x0080)
				length_utf8++;
			else if (ucs2[pos] < 0x0800)
				length_utf8 += 2;
			else
				length_utf8 += 3;
		}
	
		// Perform conversion:
	
		std::string utf8(length_utf8, ' ');
		std::string::size_type pos_utf8 = 0;
		for (pos = 0; pos < length_ucs2; pos++)
		{
			if (ucs2[pos] < 0x0080)
			{
				utf8[pos_utf8++] = (char) ucs2[pos];
			}
			else if (ucs2[pos] < 0x0800)
			{
				utf8[pos_utf8++] = 0xc0 + (ucs2[pos] >> 6);
				utf8[pos_utf8++] = 0x80 + (ucs2[pos] & 0x3f);
			}
			else
			{
				utf8[pos_utf8++] = 0xe0 + (ucs2[pos] >> 12);
				utf8[pos_utf8++] = 0x80 + ((ucs2[pos] >> 6) & 0x3f);
				utf8[pos_utf8++] = 0x80 + (ucs2[pos] & 0x3f);
			}
		}

		return utf8;
	}

	std::wstring StringHelp::latin1_to_ucs2(const std::string &latin1)
	{
		std::wstring::size_type i, length = latin1.length();
		std::wstring ucs2(length, ' ');
		for (i=0; i<length; i++)
			ucs2[i] = latin1[i];
		return ucs2;
	}

	std::wstring StringHelp::latin9_to_ucs2(const std::string &latin9)
	{
		std::wstring::size_type i, length = latin9.length();
		std::wstring ucs2(length, ' ');
		for (i=0; i<length; i++)
			ucs2[i] = ((unsigned char) latin9[i] != 0xa4) ? latin9[i] : 0x20ac;
		return ucs2;
	}

	std::string StringHelp::ucs2_to_text(const std::wstring &ucs2)
	{
		return ucs2_to_utf8(ucs2);
	}

	std::wstring StringHelp::local8_to_ucs2(const std::string &local8)
	{
		return latin9_to_ucs2(local8);
	}

	std::wstring StringHelp::utf8_to_ucs2(const std::string &utf8)
	{
		// Calculate length:

		std::wstring::size_type length_ucs2 = 0;
		std::string::size_type length_utf8 = utf8.length();
		std::string::size_type pos = 0;
		while (pos < length_utf8)
		{
			unsigned char c = utf8[pos++];
			int trailing_bytes = trailing_bytes_for_utf8[c];
			length_ucs2++;
			pos += trailing_bytes;
		}
		if (pos != length_utf8)
		{
			// error in utf8 string
			length_ucs2--;
		}
		if (pos <= 0)
			return std::wstring();

		// Perform conversion:
	
		std::wstring ucs2(length_ucs2, L'?');
		pos = 0;
		std::wstring::size_type ucs2_pos = 0;
		while (pos < length_utf8 && ucs2_pos < length_ucs2)
		{
			unsigned char c = utf8[pos++];
			int trailing_bytes = trailing_bytes_for_utf8[c];
			unsigned int ucs4 = (c & bitmask_leadbyte_for_utf8[trailing_bytes]);
			for (int i=0; i<trailing_bytes; i++)
			{
				c = (unsigned char) utf8[pos+i];
				if (c < 0xc0)
				{
					ucs4 = (ucs4 << 6) + (c & 0x3f);
				}
				else
				{
					// error in utf8 string
					ucs4 = L'?';
					break;
				}
			}
			if (ucs4 > 0 && ucs4 <= 0xffff)
				ucs2[ucs2_pos] = ucs4;
			else
				ucs2[ucs2_pos] = L'?';

			ucs2_pos++;
			pos += trailing_bytes;
		}
		return ucs2;
	}

	std::string StringHelp::utf8_to_text(const std::string &utf8)
	{
		return utf8;
	}

	std::string StringHelp::text_to_cp437(const std::string &text)
	{
		return ucs2_to_cp437(local8_to_ucs2(text));
	}

	std::string StringHelp::cp437_to_text(const std::string &cp437)
	{
		return ucs2_to_utf8(cp437_to_ucs2(cp437));
	}

	std::wstring StringHelp::cp437_to_ucs2(const std::string &cp437)
	{
		static std::wstring::value_type cp437_charset[] =
		{
			0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
			0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
			0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
			0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
			0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
			0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
			0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
			0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
			0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
			0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
			0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
			0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
			0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
			0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
			0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
			0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
			0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
			0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
			0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
			0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
			0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
			0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
			0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
			0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
			0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
			0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
			0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
			0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
			0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
			0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
			0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
			0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
		};

		DataBuffer buffer(cp437.length() * sizeof(std::wstring::value_type));
		const unsigned char *input = (const unsigned char *) cp437.data();
		std::wstring::value_type *output = (std::wstring::value_type *) buffer.get_data();
		std::wstring::size_type index, size;
		size = cp437.length();
		for (index = 0; index < size; index++)
			output[index] = cp437_charset[input[index]];
		return std::wstring(output, cp437.length());
	}

	std::string StringHelp::ucs2_to_cp437(const std::wstring &text)
	{
		std::string str8;
		str8.resize(text.length());
		unsigned char *output = (unsigned char *) str8.data();
		const std::wstring::value_type *input = text.data();
		std::wstring::size_type index, size;
		size = text.length();
		for (index = 0; index < size; index++)
		{
			if (input[index] >= 0x00 && input[index] <= 0x7f)
			{
				str8[index] = text[index];
				continue;
			}

			if (input[index] == 0x00C7) { output[index] = 0x80; continue; }
			if (input[index] == 0x00FC) { output[index] = 0x81; continue; }
			if (input[index] == 0x00E9) { output[index] = 0x82; continue; }
			if (input[index] == 0x00E2) { output[index] = 0x83; continue; }
			if (input[index] == 0x00E4) { output[index] = 0x84; continue; }
			if (input[index] == 0x00E0) { output[index] = 0x85; continue; }
			if (input[index] == 0x00E5) { output[index] = 0x86; continue; }
			if (input[index] == 0x00E7) { output[index] = 0x87; continue; }
			if (input[index] == 0x00EA) { output[index] = 0x88; continue; }
			if (input[index] == 0x00EB) { output[index] = 0x89; continue; }
			if (input[index] == 0x00E8) { output[index] = 0x8A; continue; }
			if (input[index] == 0x00EF) { output[index] = 0x8B; continue; }
			if (input[index] == 0x00EE) { output[index] = 0x8C; continue; }
			if (input[index] == 0x00EC) { output[index] = 0x8D; continue; }
			if (input[index] == 0x00C4) { output[index] = 0x8E; continue; }
			if (input[index] == 0x00C5) { output[index] = 0x8F; continue; }
			if (input[index] == 0x00C9) { output[index] = 0x90; continue; }
			if (input[index] == 0x00E6) { output[index] = 0x91; continue; }
			if (input[index] == 0x00C6) { output[index] = 0x92; continue; }
			if (input[index] == 0x00F4) { output[index] = 0x93; continue; }
			if (input[index] == 0x00F6) { output[index] = 0x94; continue; }
			if (input[index] == 0x00F2) { output[index] = 0x95; continue; }
			if (input[index] == 0x00FB) { output[index] = 0x96; continue; }
			if (input[index] == 0x00F9) { output[index] = 0x97; continue; }
			if (input[index] == 0x00FF) { output[index] = 0x98; continue; }
			if (input[index] == 0x00D6) { output[index] = 0x99; continue; }
			if (input[index] == 0x00DC) { output[index] = 0x9A; continue; }
			if (input[index] == 0x00A2) { output[index] = 0x9B; continue; }
			if (input[index] == 0x00A3) { output[index] = 0x9C; continue; }
			if (input[index] == 0x00A5) { output[index] = 0x9D; continue; }
			if (input[index] == 0x20A7) { output[index] = 0x9E; continue; }
			if (input[index] == 0x0192) { output[index] = 0x9F; continue; }
			if (input[index] == 0x00E1) { output[index] = 0xA0; continue; }
			if (input[index] == 0x00ED) { output[index] = 0xA1; continue; }
			if (input[index] == 0x00F3) { output[index] = 0xA2; continue; }
			if (input[index] == 0x00FA) { output[index] = 0xA3; continue; }
			if (input[index] == 0x00F1) { output[index] = 0xA4; continue; }
			if (input[index] == 0x00D1) { output[index] = 0xA5; continue; }
			if (input[index] == 0x00AA) { output[index] = 0xA6; continue; }
			if (input[index] == 0x00BA) { output[index] = 0xA7; continue; }
			if (input[index] == 0x00BF) { output[index] = 0xA8; continue; }
			if (input[index] == 0x2310) { output[index] = 0xA9; continue; }
			if (input[index] == 0x00AC) { output[index] = 0xAA; continue; }
			if (input[index] == 0x00BD) { output[index] = 0xAB; continue; }
			if (input[index] == 0x00BC) { output[index] = 0xAC; continue; }
			if (input[index] == 0x00A1) { output[index] = 0xAD; continue; }
			if (input[index] == 0x00AB) { output[index] = 0xAE; continue; }
			if (input[index] == 0x00BB) { output[index] = 0xAF; continue; }
			if (input[index] == 0x2591) { output[index] = 0xB0; continue; }
			if (input[index] == 0x2592) { output[index] = 0xB1; continue; }
			if (input[index] == 0x2593) { output[index] = 0xB2; continue; }
			if (input[index] == 0x2502) { output[index] = 0xB3; continue; }
			if (input[index] == 0x2524) { output[index] = 0xB4; continue; }
			if (input[index] == 0x2561) { output[index] = 0xB5; continue; }
			if (input[index] == 0x2562) { output[index] = 0xB6; continue; }
			if (input[index] == 0x2556) { output[index] = 0xB7; continue; }
			if (input[index] == 0x2555) { output[index] = 0xB8; continue; }
			if (input[index] == 0x2563) { output[index] = 0xB9; continue; }
			if (input[index] == 0x2551) { output[index] = 0xBA; continue; }
			if (input[index] == 0x2557) { output[index] = 0xBB; continue; }
			if (input[index] == 0x255D) { output[index] = 0xBC; continue; }
			if (input[index] == 0x255C) { output[index] = 0xBD; continue; }
			if (input[index] == 0x255B) { output[index] = 0xBE; continue; }
			if (input[index] == 0x2510) { output[index] = 0xBF; continue; }
			if (input[index] == 0x2514) { output[index] = 0xC0; continue; }
			if (input[index] == 0x2534) { output[index] = 0xC1; continue; }
			if (input[index] == 0x252C) { output[index] = 0xC2; continue; }
			if (input[index] == 0x251C) { output[index] = 0xC3; continue; }
			if (input[index] == 0x2500) { output[index] = 0xC4; continue; }
			if (input[index] == 0x253C) { output[index] = 0xC5; continue; }
			if (input[index] == 0x255E) { output[index] = 0xC6; continue; }
			if (input[index] == 0x255F) { output[index] = 0xC7; continue; }
			if (input[index] == 0x255A) { output[index] = 0xC8; continue; }
			if (input[index] == 0x2554) { output[index] = 0xC9; continue; }
			if (input[index] == 0x2569) { output[index] = 0xCA; continue; }
			if (input[index] == 0x2566) { output[index] = 0xCB; continue; }
			if (input[index] == 0x2560) { output[index] = 0xCC; continue; }
			if (input[index] == 0x2550) { output[index] = 0xCD; continue; }
			if (input[index] == 0x256C) { output[index] = 0xCE; continue; }
			if (input[index] == 0x2567) { output[index] = 0xCF; continue; }
			if (input[index] == 0x2568) { output[index] = 0xD0; continue; }
			if (input[index] == 0x2564) { output[index] = 0xD1; continue; }
			if (input[index] == 0x2565) { output[index] = 0xD2; continue; }
			if (input[index] == 0x2559) { output[index] = 0xD3; continue; }
			if (input[index] == 0x2558) { output[index] = 0xD4; continue; }
			if (input[index] == 0x2552) { output[index] = 0xD5; continue; }
			if (input[index] == 0x2553) { output[index] = 0xD6; continue; }
			if (input[index] == 0x256B) { output[index] = 0xD7; continue; }
			if (input[index] == 0x256A) { output[index] = 0xD8; continue; }
			if (input[index] == 0x2518) { output[index] = 0xD9; continue; }
			if (input[index] == 0x250C) { output[index] = 0xDA; continue; }
			if (input[index] == 0x2588) { output[index] = 0xDB; continue; }
			if (input[index] == 0x2584) { output[index] = 0xDC; continue; }
			if (input[index] == 0x258C) { output[index] = 0xDD; continue; }
			if (input[index] == 0x2590) { output[index] = 0xDE; continue; }
			if (input[index] == 0x2580) { output[index] = 0xDF; continue; }
			if (input[index] == 0x03B1) { output[index] = 0xE0; continue; }
			if (input[index] == 0x00DF) { output[index] = 0xE1; continue; }
			if (input[index] == 0x0393) { output[index] = 0xE2; continue; }
			if (input[index] == 0x03C0) { output[index] = 0xE3; continue; }
			if (input[index] == 0x03A3) { output[index] = 0xE4; continue; }
			if (input[index] == 0x03C3) { output[index] = 0xE5; continue; }
			if (input[index] == 0x00B5) { output[index] = 0xE6; continue; }
			if (input[index] == 0x03C4) { output[index] = 0xE7; continue; }
			if (input[index] == 0x03A6) { output[index] = 0xE8; continue; }
			if (input[index] == 0x0398) { output[index] = 0xE9; continue; }
			if (input[index] == 0x03A9) { output[index] = 0xEA; continue; }
			if (input[index] == 0x03B4) { output[index] = 0xEB; continue; }
			if (input[index] == 0x221E) { output[index] = 0xEC; continue; }
			if (input[index] == 0x03C6) { output[index] = 0xED; continue; }
			if (input[index] == 0x03B5) { output[index] = 0xEE; continue; }
			if (input[index] == 0x2229) { output[index] = 0xEF; continue; }
			if (input[index] == 0x2261) { output[index] = 0xF0; continue; }
			if (input[index] == 0x00B1) { output[index] = 0xF1; continue; }
			if (input[index] == 0x2265) { output[index] = 0xF2; continue; }
			if (input[index] == 0x2264) { output[index] = 0xF3; continue; }
			if (input[index] == 0x2320) { output[index] = 0xF4; continue; }
			if (input[index] == 0x2321) { output[index] = 0xF5; continue; }
			if (input[index] == 0x00F7) { output[index] = 0xF6; continue; }
			if (input[index] == 0x2248) { output[index] = 0xF7; continue; }
			if (input[index] == 0x00B0) { output[index] = 0xF8; continue; }
			if (input[index] == 0x2219) { output[index] = 0xF9; continue; }
			if (input[index] == 0x00B7) { output[index] = 0xFA; continue; }
			if (input[index] == 0x221A) { output[index] = 0xFB; continue; }
			if (input[index] == 0x207F) { output[index] = 0xFC; continue; }
			if (input[index] == 0x00B2) { output[index] = 0xFD; continue; }
			if (input[index] == 0x25A0) { output[index] = 0xFE; continue; }
			if (input[index] == 0x00A0) { output[index] = 0xFF; continue; }
			output[index] = '_';
		}
		return str8;
	}

	StringHelp::BOMType StringHelp::detect_bom(const void *data, std::string::size_type length)
	{
		const unsigned char utf32_be[] = { 0x00, 0x00, 0xfe, 0xff };
		const unsigned char utf32_le[] = { 0xff, 0xfe, 0x00, 0x00 };
		const unsigned char utf16_be[] = { 0xfe, 0xff };
		const unsigned char utf16_le[] = { 0xff, 0xfe };
		const unsigned char utf8[] = { 0xef, 0xbb, 0xbf };

		if (length >= 3 && memcmp(data, utf8, 3) == 0)
			return bom_utf8;
		else if (length >= 2 && memcmp(data, utf16_le, 2) == 0)
			return bom_utf16_le;
		else if (length >= 2 && memcmp(data, utf16_be, 2) == 0)
			return bom_utf16_be;
		else if (length >= 4 && memcmp(data, utf32_le, 4) == 0)
			return bom_utf32_le;
		else if (length >= 4 && memcmp(data, utf32_be, 4) == 0)
			return bom_utf32_be;
		else
			return bom_none;
	}

	std::string::size_type StringHelp::utf8_length(const std::string &str)
	{
		std::string::size_type len = 0;
		UTF8_Reader utf8_reader(str.data(), str.length());
		while(!utf8_reader.is_end())
		{
			len++;
			utf8_reader.next();
		}

		return len;
	}

	const char StringHelp::trailing_bytes_for_utf8[256] =
	{
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
		2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
	};

	const unsigned char StringHelp::bitmask_leadbyte_for_utf8[6] =
	{
		0x7f,
		0x1f,
		0x0f,
		0x07,
		0x03,
		0x01
	};

	#ifndef WIN32
	#ifndef HAVE_WCSCASECMP
	int
	cl_wcscasecmp(const wchar_t *s1, const wchar_t *s2)
	{
		const wchar_t *us1 = (const wchar_t *)s1;
		const wchar_t *us2 = (const wchar_t *)s2;

		while (towlower(*us1) == towlower(*us2++))
			if (*us1++ == '\0')
				return (0);
		return (towlower(*us1) - towlower(*--us2));
	}
	#endif
	#endif
}
