/*
 * Copyright (c) 2005-2006, Tom N Harris.
 *
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
 *
 *     A copy of the GNU General Public License can be found at
 *     http://www.gnu.org/
 *
 * Written by Tom N Harris <telliamed@whoopdedo.org>
 *
 */

#include <cstdlib>
#include "win32.h"

#include "cString.h"
#include "UniConversion.h"

using namespace Setup;
using namespace std;

String String::Format(LPCTSTR fmt, ...)
{
	TCHAR *Buff;
	va_list arglist;
	DWORD numchars;

	if (fmt == NULL)
		return String();

	va_start(arglist, fmt);
	numchars = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
				   fmt, 0, 0, (LPTSTR)&Buff, 0, &arglist);
	va_end(arglist);

	if (numchars == 0)
	{
		// Something went wrong.
		return String();
	}

	String s(Buff, numchars);
	::LocalFree(Buff);

	return s;
}

String String::Format(unsigned int i, ...)
{
	TCHAR StringBuffer[1024];
	TCHAR *Buff;
	va_list arglist;
	DWORD numchars;

	// Get the string from the stringtable (FormatMessage() can only work with
	// literal strings or *message*table entries, which are different for some
	// inexplicable reason).
	::LoadString(::GetModuleHandle(NULL), i, StringBuffer, 1024);

	va_start(arglist, i);
	numchars = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
				   StringBuffer, 0, 0, (LPTSTR)&Buff, 0, &arglist);
	va_end(arglist);

	if (numchars == 0)
	{
		// Something went wrong.
		return String();
	}

	String s(Buff, numchars);
	::LocalFree(Buff);

	return s;
}

String String::Load(unsigned int i)
{
	TCHAR StringBuffer[1024];

	// Get the string from the stringtable (FormatMessage() can only work with
	// literal strings or *message*table entries, which are different for some
	// inexplicable reason).
	::LoadString(::GetModuleHandle(NULL), i, StringBuffer, 1024);

	String s(StringBuffer);

	return s;
}

#ifdef UNICODE
String::String(string const& __s)
	: _M_conv(NULL)
{
	if (__s.empty())
		return;
	size_t wlen = UCS2Length(__s.data(), __s.size());
	reserve(wlen);
	char const* cdata = __s.c_str();
	for (char const* c = cdata; size_t(c - cdata) < __s.size(); c = UTF8NextChar(c))
		push_back(UCS2FromUTF8(c));
}

String::String(string const& __s, size_t __p, size_t __n)
	: _M_conv(NULL)
{
	if (__p >= __s.size())
		return;
	size_t clen = (__n < __s.size() - __p) ? __n : __s.size() - __p;
	char const* cdata = __s.c_str() + __p;
	size_t wlen = UCS2Length(cdata, clen);
	reserve(wlen);
	for (char const* c = cdata; size_t(c - cdata) < clen; c = UTF8NextChar(c))
		push_back(UCS2FromUTF8(c));
}

String::String(char const* __s)
	: _M_conv(NULL)
{
	if (__s == NULL)
		return;
	size_t clen = char_traits<char>::length(__s);
	size_t wlen = UCS2Length(__s, clen);
	reserve(wlen);
	for (char const* c = __s; size_t(c - __s) < clen; c = UTF8NextChar(c))
		push_back(UCS2FromUTF8(c));
}

String::String(char const* __s, size_t __n)
	: _M_conv(NULL)
{
	if (__s == NULL || __n == 0)
		return;
	size_t wlen = UCS2Length(__s, __n);
	reserve(wlen);
	for (char const* c = __s; size_t(c - __s) < __n; c = UTF8NextChar(c))
		push_back(UCS2FromUTF8(c));
}

String::String(size_t __n, char __c)
	: wstring(__n, wchar_t(__c)), _M_conv(NULL)
{
}

String& String::operator=(string const& __s)
{
	clear();
	if (__s.size() == 0)
		return *this;
	size_t wlen = UCS2Length(__s.data(), __s.size());
	reserve(wlen);
	char const* cdata = __s.c_str();
	for (char const* c = cdata; size_t(c - cdata) < __s.size(); c = UTF8NextChar(c))
		push_back(UCS2FromUTF8(c));
	return *this;
}

String& String::operator=(char const* __s)
{
	clear();
	if (__s == NULL)
		return *this;
	size_t clen = char_traits<char>::length(__s);
	size_t wlen = UCS2Length(__s, clen);
	reserve(wlen);
	for (char const* c = __s; size_t(c - __s) < clen; c = UTF8NextChar(c))
		push_back(UCS2FromUTF8(c));
	return *this;
}

String& String::operator=(char __c)
{
	assign(1, wchar_t(__c));
	return *this;
}

char const* String::to_utf8()
{
	::operator delete(_M_conv);
	size_t clen = UTF8Length(data(), size());
	_M_conv = reinterpret_cast<char*>(::operator new(clen + 1));
	UTF8FromUCS2(data(), size(), _M_conv, clen);
	return _M_conv;
}
#else
String::String(wstring const& __s)
	: _M_conv(NULL)
{
	if (__s.size() == 0)
		return;
	char ch[4];
	size_t clen = UTF8Length(__s.data(), __s.size());
	reserve(clen);
	const wchar_t* uch = __s.c_str();
	for (size_t n = 0; n < __s.size(); ++n, ++uch)
	{
		size_t i = UTF8FromUCS2(*uch, ch, 4);
		append(ch, i);
	}
}

String::String(wstring const& __s, size_t __p, size_t __n)
	: _M_conv(NULL)
{
	if (__p >= __s.size())
		return;
	char ch[4];
	size_t wlen = (__n < __s.size() - __p) ? __n : __s.size() - __p;
	wchar_t const* uch = __s.c_str() + __p;
	size_t clen = UTF8Length(uch, wlen);
	reserve(clen);
	for (size_t n = 0; n < wlen; ++n, ++uch)
	{
		size_t i = UTF8FromUCS2(*uch, ch, 4);
		append(ch, i);
	}
}

String::String(wchar_t const* __s)
	: _M_conv(NULL)
{
	if (__s == NULL)
		return;
	char ch[4];
	size_t wlen = char_traits<wchar_t>::length(__s);
	size_t clen = UTF8Length(__s, wlen);
	reserve(clen);
	const wchar_t* uch = __s;
	for (size_t n = 0; n < wlen; ++n, ++uch)
	{
		size_t i = UTF8FromUCS2(*uch, ch, 4);
		append(ch, i);
	}
}

String::String(wchar_t const* __s, size_t __n)
	: _M_conv(NULL)
{
	if (__s == NULL || __n == 0)
		return;
	char ch[4];
	size_t clen = UTF8Length(__s, __n);
	reserve(clen);
	const wchar_t* uch = __s;
	for (size_t n = 0; n < __n; ++n, ++uch)
	{
		size_t i = UTF8FromUCS2(*uch, ch, 4);
		append(ch, i);
	}
}

String::String(size_t __n, wchar_t __c)
	: _M_conv(NULL)
{
	if (__n == 0)
		return;
	char ch[4];
	size_t i = UTF8FromUCS2(__c, ch, 4);
	reserve(i * __n);
	while (__n--)
		append(ch, i);
}

String& String::operator=(wstring const& __s)
{
	clear();
	if (__s.size() == 0)
		return *this;
	char ch[4];
	size_t clen = UTF8Length(__s.data(), __s.size());
	reserve(clen);
	const wchar_t* uch = __s.c_str();
	for (size_t n = 0; n < __s.size(); ++n, ++uch)
	{
		size_t i = UTF8FromUCS2(*uch, ch, 4);
		append(ch, i);
	}
	return *this;
}

String& String::operator=(wchar_t const* __s)
{
	clear();
	if (__s == NULL)
		return *this;
	char ch[4];
	size_t wlen = char_traits<wchar_t>::length(__s);
	size_t clen = UTF8Length(__s, wlen);
	reserve(clen);
	const wchar_t* uch = __s;
	for (size_t n = 0; n < wlen; ++n, ++uch)
	{
		size_t i = UTF8FromUCS2(*uch, ch, 4);
		append(ch, i);
	}
	return *this;
}

String& String::operator=(wchar_t __c)
{
	char ch[4];
	size_t i = UTF8FromUCS2(__c, ch, 4);
	assign(ch, i);
	return *this;
}

wchar_t const* String::to_ucs2()
{
	::operator delete(_M_conv);
	size_t wlen = UCS2Length(data(), size());
	_M_conv = reinterpret_cast<wchar_t*>(::operator new((wlen+1) * sizeof(wchar_t)));
	wlen = UCS2FromUTF8(data(), size(), _M_conv, wlen);
	_M_conv[wlen] = 0;
	return _M_conv;
}
#endif

