/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

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 3 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#include "hsStlUtils.h"

// stl extensions
namespace xtl {

//std::string
std::string & trimleft(std::string & s, const char * charset)
{
	s.erase(0, s.find_first_not_of(charset));
	return s;
}

std::wstring & trimleft(std::wstring & s, const wchar_t * charset)
{
	s.erase(0, s.find_first_not_of(charset));
	return s;
}

std::string & trimright(std::string & s, const char * charset)
{
	int idx = s.find_last_not_of(charset);
	
	if (std::string::npos == idx)
	{
		s.erase();
	}
	else
	{
		char c    = s.at(idx);
		s.erase(idx, std::string::npos);    
		s.append(1, c);
	}
	
	return s;
}

std::wstring & trimright(std::wstring & s, const wchar_t * charset)
{
	int idx = s.find_last_not_of(charset);

	if (std::wstring::npos == idx)
	{
		s.erase();
	}
	else
	{
		wchar_t c = s.at(idx);
		s.erase(idx, std::string::npos);
		s.append(1, c);
	}

	return s;
}

std::string & trim(std::string & s, const char * charset)
{
	trimleft(s,charset);
	trimright(s,charset);
	return s;
}

std::wstring & trim(std::wstring & s, const wchar_t * charset)
{
	trimleft(s,charset);
	trimright(s,charset);
	return s;
}

//xtl::istring
xtl::istring & trimleft(xtl::istring & s, const char * charset)
{
	s.erase(0, s.find_first_not_of(charset));
	return s;
}

xtl::iwstring & trimleft(xtl::iwstring & s, const wchar_t * charset)
{
	s.erase(0, s.find_first_not_of(charset));
	return s;
}

xtl::istring & trimright(xtl::istring & s, const char * charset)
{
	int idx = s.find_last_not_of(charset);
	
	if (xtl::istring::npos == idx)
	{
		s.erase();
	}
	else
	{
		char c    = s.at(idx);
		s.erase(idx, xtl::istring::npos);    
		s.append(1, c);
	}
	
	return s;
}

xtl::iwstring & trimright(xtl::iwstring & s, const wchar_t * charset)
{
	int idx = s.find_last_not_of(charset);

	if (xtl::iwstring::npos == idx)
	{
		s.erase();
	}
	else
	{
		wchar_t c = s.at(idx);
		s.erase(idx, xtl::iwstring::npos);
		s.append(1, c);
	}
	
	return s;
}

xtl::istring & trim(xtl::istring & s, const char * charset)
{
	trimleft(s,charset);
	trimright(s,charset);
	return s;
}

xtl::iwstring & trim(xtl::iwstring & s, const wchar_t * charset)
{
	trimleft(s,charset);
	trimright(s,charset);
	return s;
}

// c-string
std::string trim(const char * s, const char * charset)
{
	std::string result  = s;
	trimleft(result,charset);
	trimright(result,charset);
	return result;
}

std::wstring trim(const wchar_t * s, const wchar_t * charset)
{
	std::wstring result = s;
	trimleft(result,charset);
	trimright(result,charset);
	return result;
}


// format
std::string format(const char * fmt, ...)
{
	std::string result;
	va_list args;
	va_start(args,fmt);
	formatv(result,fmt,args);
	va_end(args);
	return result;
}

std::wstring format(const wchar_t * fmt, ...)
{
	std::wstring result;
	va_list args;
	va_start(args,fmt);
	formatv(result,fmt,args);
	va_end(args);
	return result;
}

std::string formatv(const char * fmt, va_list args)
{
	std::string result;
	formatv( result, fmt, args );
	return result;
}

std::wstring formatv(const wchar_t * fmt, va_list args)
{
	std::wstring result;
	formatv( result, fmt, args );
	return result;
}

bool format(std::string & out, const char * fmt, ...)
{
	va_list args;
	va_start(args,fmt);
	bool r = formatv(out,fmt,args);
	va_end(args);
	return r;
}

bool format(std::wstring & out, const wchar_t * fmt, ...)
{
	va_list args;
	va_start(args,fmt);
	bool r = formatv(out,fmt,args);
	va_end(args);
	return r;
}

bool formatv(std::string & out, const char * fmt, va_list args)
{
#define kBufSz 2048

	char buf[kBufSz];
	char * pbuf = buf;
	int len = 0;
	int attempts = 0;
	bool success = false;
	const int kMaxAttempts = 40;

	do
	{
		int maxlen = kBufSz*attempts+kBufSz-1;
		len = hsVsnprintf(pbuf,maxlen,fmt,args);
		attempts++;
		success = (len>=0 && len<maxlen);
		if (!success)
		{
			if (pbuf!=buf)
				delete [] pbuf;
			pbuf = TRACKED_NEW char[kBufSz+kBufSz*attempts];
		}
	}
	while (!success && attempts<kMaxAttempts);

	if (success)
	{
		pbuf[len] = '\0';
		out = pbuf;
	}

	if (success)
	{
		pbuf[len] = '\0';
		out = pbuf;
	}
	else
	{
		out = "";
		if ( attempts==kMaxAttempts )
		{
			hsDebugMessage( "xtl::formatv - Max reallocs occurred while formatting string. Result is likely truncated!", 0 );
		}
	}

	if (pbuf!=buf)
		delete [] pbuf;

	return success;
}

bool formatv(std::wstring & out, const wchar_t * fmt, va_list args)
{
#define kBufSz 2048
	
	wchar_t buf[kBufSz];
	wchar_t * pbuf = buf;
	int len = 0;
	int attempts = 0;
	bool success = false;
	const int kMaxAttempts = 40;
	
	do
	{
		int maxlen = kBufSz*attempts+kBufSz-1;
		len = hsVsnwprintf(pbuf,maxlen,fmt,args);
		attempts++;
		success = (len>=0 && len<maxlen);
		if (!success)
		{
			if (pbuf!=buf)
				delete [] pbuf;
			pbuf = TRACKED_NEW wchar_t[kBufSz+kBufSz*attempts];
		}
	}
	while (!success && attempts<kMaxAttempts);
	
	if (success)
	{
		pbuf[len] = L'\0';
		out = pbuf;
	}
	
	if (success)
	{
		pbuf[len] = L'\0';
		out = pbuf;
	}
	else
	{
		out = L"";
		if ( attempts==kMaxAttempts )
		{
			hsDebugMessage( "xtl::formatv - Max reallocs occurred while formatting wstring. Result is likely truncated!", 0 );
		}
	}
	
	if (pbuf!=buf)
		delete [] pbuf;
	
	return success;
}

typedef std::vector<std::string> StringVector;
typedef std::vector<std::wstring> WStringVector;
typedef std::list<std::string> StringList;
typedef std::list<std::wstring> WStringList;
typedef std::set<std::string> StringSet;
typedef std::set<std::wstring> WStringSet;

template bool GetStringGroup<StringList>(const std::string& s, StringList& group, char sep);
template bool GetStringGroup<WStringList>(const std::wstring& s, WStringList& group, wchar_t sep);
template bool GetStringGroup<StringVector>(const std::string& s, StringVector& group, char sep);
template bool GetStringGroup<WStringVector>(const std::wstring& s, WStringVector& group, wchar_t sep);
template bool GetStringGroup<StringSet>(const std::string& s, StringSet& group, char sep);
template bool GetStringGroup<WStringSet>(const std::wstring& s, WStringSet& group, wchar_t sep);

template <typename T> bool GetStringGroup(const std::string& s, T& group, char sep)
{
	bool ret = false;
	std::string::size_type oldpos = 0, newpos = 0;
	
	if (!(s.empty()))
	{
		do 
		{
			newpos = s.find(',',oldpos);
			group.insert(group.end(),s.substr(oldpos,newpos));
			if (newpos != s.npos)
				oldpos = newpos+1;
		} 
		while(newpos != s.npos);
		ret = true;
	}

	return ret;
}

template <typename T> bool GetStringGroup(const std::wstring& s, T& group, wchar_t sep)
{
	bool ret = false;
	std::wstring::size_type oldpos = 0, newpos = 0;

	if (!(s.empty()))
	{
		do
		{
			newpos = s.find(L',',oldpos);
			group.insert(group.end(),s.substr(oldpos,newpos));
			if (newpos != s.npos)
				oldpos = newpos+1;
		} while(newpos != s.npos);
		ret = true;
	}

	return ret;
}


template bool GetStringGroupAsString<StringList>(const StringList& group, std::string& s, char sep);
template bool GetStringGroupAsString<WStringList>(const WStringList& group, std::wstring& s, wchar_t sep);
template bool GetStringGroupAsString<StringVector>(const StringVector& group, std::string& s, char sep);
template bool GetStringGroupAsString<WStringVector>(const WStringVector& group, std::wstring& s, wchar_t sep);
template bool GetStringGroupAsString<StringSet>(const StringSet& group, std::string& s, char sep);
template bool GetStringGroupAsString<WStringSet>(const WStringSet& group, std::wstring& s, wchar_t sep);

template <typename T> bool GetStringGroupAsString(const T& group, std::string& s, char sep)
{
	typename T::const_iterator it = group.begin();
	bool fst = true;
	while (it != group.end())
	{
		if (!fst)
			s += ",";
		else
			fst = false;
		s+= (*it).c_str();
		it++;
	}

	return true;
}

template <typename T> bool GetStringGroupAsString(const T& group, std::wstring& s, wchar_t sep)
{
	typename T::const_iterator it = group.begin();
	bool fst = true;
	while (it != group.end())
	{
		if (!fst)
			s += L",";
		else
			fst = false;
		s+= (*it).c_str();
		it++;
	}
	
	return true;
}


} // namespace std