#include <stdio.h>
#include <sstream>
#include <functional>
#include <stdarg.h>  // for va_start, etc
#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include "Utils.h"
#include "Containers.h"
#include "Test.h"


BEGIN_NAMESPACE


std::string wstring_to_utf8(const std::wstring& utf16)
{
	std::vector<char> utf8(100,'\0');
	int len = WideCharToMultiByte(CP_UTF8, 0, utf16.c_str(), -1, NULL, 0, 0, 0);
	if (len > 1)
	{
		utf8.resize(len + 1);
		WideCharToMultiByte(CP_UTF8, 0, utf16.c_str(), -1, utf8.data(), len, 0, 0);
	}
	return utf8.data();
}


std::wstring utf8_to_wstring(const std::string& utf8)
{
	std::vector<wchar_t> utf16(100,'\0');
	int len = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, NULL, 0);
	if (len>1)
	{ 
		utf16.resize(len + 1);
		MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, utf16.data(), len);
	}
	return utf16.data();
}


#ifndef USE_STL_CONTAINERS


Vector<std::string> split(const std::string &s, char delim)
{
    Vector<std::string> elems;
    std::stringstream ss(s);
    std::string item;
    while (std::getline(ss, item, delim)) {
        elems.push_back(item);
    }
    return elems;
}


Vector<std::string> split(const std::string &s, const char* a_delim)
{
	Vector<std::string> elems;
	std::string delim = a_delim;
	size_t start = 0U;
	size_t end = s.find(delim);
	while (end != std::string::npos)
	{
		elems.push_back(s.substr(start, end - start));
		start = end + delim.length();
		end = s.find(delim, start);
	}
	elems.push_back(s.substr(start, end));
    return elems;
}


// returns everything to the left of match from the input string
std::string left(const std::string &str, const char* match)
{
	std::size_t found = str.find(match);
	if (found == std::string::npos)
		return str; // Not found
	return str.substr(0, found);
}


// returns everything to the right of match from the input string
std::string right(const std::string &str, const char* match)
{
	std::size_t found = str.find(match);
	if (found == std::string::npos)
		return str; // Not found
	return str.substr(found + strlen(match));
}


std::string string_format(const char* fmt, ...)
{
    std::vector<char> str(100,'\0');
    va_list ap;
    while (1) {
        va_start(ap, fmt);
        int n = vsnprintf(str.data(), str.size(), fmt, ap);
        va_end(ap);
        if ((n > -1) && (size_t(n) < str.size())) {
            return str.data();
        }
        if (n > -1)
            str.resize(n + 1);
        else
            str.resize(str.size() * 2);
    }
    return str.data();
}


std::string to_string(uint32_t i)
{
    std::stringstream ss;
    ss << i;
    return ss.str();
}


Vector<char> wideString_to_utf8(const std::wstring& utf16)
{
	Vector<char> utf8(100,'\0');
	int len = WideCharToMultiByte(CP_UTF8, 0, utf16.c_str(), -1, NULL, 0, 0, 0);
	if (len > 1)
	{
		utf8.resize(len + 1);
		WideCharToMultiByte(CP_UTF8, 0, utf16.c_str(), -1, utf8.data(), len, 0, 0);
	}
	return utf8;
}


String::String()                              {}
String::~String()                             {}
String::String(char a_val)                    { std::stringstream ss; ss << a_val; m_str = ss.str(); }
String::String(int8_t a_val)                  { std::stringstream ss; ss << a_val; m_str = ss.str(); }
String::String(uint8_t a_val)                 { std::stringstream ss; ss << a_val; m_str = ss.str(); }
String::String(int16_t a_val)                 { std::stringstream ss; ss << a_val; m_str = ss.str(); }
String::String(uint16_t a_val)                { std::stringstream ss; ss << a_val; m_str = ss.str(); }
String::String(int32_t a_val)                 { std::stringstream ss; ss << a_val; m_str = ss.str(); }
String::String(uint32_t a_val)                { std::stringstream ss; ss << a_val; m_str = ss.str(); }
String::String(int64_t a_val)                 { std::stringstream ss; ss << a_val; m_str = ss.str(); }
String::String(uint64_t a_val)                { std::stringstream ss; ss << a_val; m_str = ss.str(); }
String::String(float a_val)                   { std::stringstream ss; ss << a_val; m_str = ss.str(); }
String::String(double a_val)                  { std::stringstream ss; ss << a_val; m_str = ss.str(); }

/*
String::String(const std::wstring& a_other)   { m_str = wstring_to_utf8(a_other); }
*/
String::String(const std::string& a_other)    { m_str = a_other; }
String::String(const wchar_t* a_wideString)   { m_str = wstring_to_utf8(a_wideString); }

String::String(const String& a_other)         { m_str = a_other.m_str; }

String String::replace(const char* a_match, const char* a_replacement)
{
    size_t start_pos = 0;
	String str = *this;
	size_t len = strlen(a_replacement);
	if (strlen(a_match) != len) // make sure a_match and a_replacement are the same length
		return str;
	while ((start_pos = find(a_match, start_pos)) != npos)
	{
        //str.replace(start_pos, a_match.toUtf8().length(), a_replacement.toUtf8());
        memcpy((char*)str.data() + start_pos, a_replacement, len);
        start_pos += len;
    }
    return str;
}

String String::left(const char* a_match)	  { return NAMESPACE::left(m_str, a_match); }
String String::right(const char* a_match)     { return NAMESPACE::right(m_str, a_match); }
const std::string& String::toUtf8() const     { return m_str; }
const std::wstring& String::toWString() const {	m_wstr = utf8_to_wstring(m_str); return m_wstr; }
/*
*/
const char* String::data() const              { return m_str.data(); }
const char* String::c_str() const             { return m_str.data(); }
String::operator const char*() const          { return c_str(); }

String& String::operator= (const String& a_other)  { m_str  = a_other.m_str; return *this; }
String& String::operator= (const char* a_str)      { m_str  = a_str;         return *this; }
String& String::operator= (char a_ch)              { m_str  = a_ch;          return *this; }
String& String::operator+=(const char* a_str)      { m_str += a_str;         return *this; }
String& String::operator+=(const String& a_other)  { m_str += a_other.m_str; return *this; }
String& String::operator+=(char a_ch)              { m_str += a_ch;          return *this; }

bool String::operator==(const char* a_other) const           { return (m_str == std::string(a_other)); }
//bool String::operator==(const std::string& a_other) const    { return (m_str == a_other); }
bool String::operator==(const String& a_other) const         { return (m_str == a_other.m_str); }
bool String::operator!=(const char* a_other) const           { return (m_str != std::string(a_other)); }
//bool String::operator!=(const std::string& a_other) const    { return (m_str != a_other); }
bool String::operator!=(const String& a_other) const         { return (m_str != a_other.m_str); }
bool String::operator< (const String& a_other) const         { return (m_str <  a_other.m_str); }
bool String::operator> (const String& a_other) const         { return (m_str >  a_other.m_str); }
bool String::operator<=(const String& a_other) const         { return (m_str <= a_other.m_str); }
bool String::operator>=(const String& a_other) const         { return (m_str >= a_other.m_str); }
bool String::operator!() const                               { return m_str.size() ? false : true; }

const String operator+(const String& a_a, const String& a_b) { return String(a_a.m_str + a_b.m_str); }
const String operator+(const String& a_a, const char* a_b)   { return String(a_a.m_str + a_b); }
const String operator+(const char* a_a, const String& a_b)   { return String(a_a + a_b.m_str); }
const String operator+(char a_a, const String& a_b)          { return String(a_a + a_b.m_str); }
const String operator+(const String& a_a, char a_b)          { return String(a_a.m_str + a_b); }
bool operator==(const char* a_a, const String& a_b)          { return a_a == a_b.m_str; }
bool operator!=(const char* a_a, const String& a_b)          { return a_a != a_b.m_str; }


const size_t String::npos = -1;


static size_t hashFunc(const uint8_t *a_str, size_t a_count)
{
	size_t val = 2166136261U;
	for (size_t i = 0; i < a_count; i++)
	{
		val ^= (size_t)a_str[i];
		val *= 16777619U;
	}
	//val ^= val >> 32;
	return val;
}


size_t String::hash() const
{
	return hashFunc((const uint8_t *)m_str.data(), m_str.size());
    //return std::hash<std::string>()(m_str);
}


size_t String::find(const char* a_match, size_t a_start)
{
	int len = strlen(a_match);
	int siz = m_str.size();
	const char* data = m_str.data();
	if (siz < len)
		return npos;
	for (size_t i = a_start; i < (siz-len); i++)
	{
		size_t j = 0;
		for (; j < len; j++)
			if (data[i+j] != a_match[j])
				break;
		if (j == len)
			return i;
	}
	return npos;
}


Vector<String> String::split(const char* a_deliminator)
{
	Vector<String> elems;
	size_t start = 0U;
	size_t end = find(a_deliminator);
	int len = strlen(a_deliminator);
	while (end != npos)
	{
		elems.push_back(String(m_str.substr(start, end - start)));
		start = end + len;
		end = find(a_deliminator, start);
	}
	elems.push_back(String(m_str.substr(start, end)));
    return elems;
}


String::String(const char* fmt, ...)
{
    std::vector<char> str(50,'\0');
    va_list ap;
	int n = -1;
    while ((n <= -1) || (size_t(n) >= str.size())) {
		str.resize((n <= -1) ? (str.size() * 2) : (n + 1));
        va_start(ap, fmt);
        n = vsnprintf(str.data(), str.size(), fmt, ap);
        va_end(ap);
    }
    m_str = str.data();
}


UNIT_TEST(StringTests, 0)
{
	String str("blah/foo/bar");
	Vector<String> strs = str.split("/");

	CHECK(str == "blah/foo/bar");
	CHECK(strs.size() == 3);
	CHECK(strs[0] == "blah");
	CHECK(strs[1] == "foo");
	CHECK(strs[2] == "bar");
	CHECK(str.left("foo") == "blah/");
	CHECK(str.right("foo") == "/bar");
}


#else


const std::wstring& String::toWString() const
{
	m_wstr = utf8_to_wstring(*this);
	return m_wstr;
}


#endif



#if 0


// whole bunch of code trying to make cout and stdout write to visual studios output window
// only cout seems to be able to be made to do this, looks like nearly impossible to do to stdout

#include <iostream>
#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
#include <windows.h>

/// \brief This class is a derivate of basic_stringbuf which will output
/// all the written data using the OutputDebugString function
template<typename TChar, typename TTraits = std::char_traits<TChar>>
class OutputDebugStringBuf : public std::basic_stringbuf<TChar,TTraits> {
public:
	explicit OutputDebugStringBuf() : _buffer(256) {
		setg(nullptr, nullptr, nullptr);
		setp(_buffer.data(), _buffer.data(), _buffer.data() + _buffer.size());
	}

	~OutputDebugStringBuf() {
		int x = 0;
	}

	static_assert(std::is_same<TChar,char>::value || std::is_same<TChar,wchar_t>::value, "OutputDebugStringBuf only supports char and wchar_t types");

	int sync() try {
		MessageOutputer<TChar,TTraits>()(pbase(), pptr());
		setp(_buffer.data(), _buffer.data(), _buffer.data() + _buffer.size());
		return 0;
	} catch(...) {
		return -1;
	}

	int_type overflow(int_type c = TTraits::eof()) {
		auto syncRet = sync();
		if (c != TTraits::eof()) {
			_buffer[0] = c;
			setp(_buffer.data(), _buffer.data() + 1, _buffer.data() + _buffer.size());
		}
		return syncRet == -1 ? TTraits::eof() : 0;
	}


private:
	std::vector<TChar>		_buffer;

	template<typename TChar, typename TTraits>
	struct MessageOutputer;

	template<>
	struct MessageOutputer<char,std::char_traits<char>> {
		template<typename TIterator>
		void operator()(TIterator begin, TIterator end) const {
			std::string s(begin, end);
			OutputDebugStringA(s.c_str());
		}
	};

	template<>
	struct MessageOutputer<wchar_t,std::char_traits<wchar_t>> {
		template<typename TIterator>
		void operator()(TIterator begin, TIterator end) const {
			std::wstring s(begin, end);
			OutputDebugStringW(s.c_str());
		}
	};

};


#include <cstdio>
void initDebugOutput()
{
	static OutputDebugStringBuf<char> charDebugOutput;
	std::cout.rdbuf(&charDebugOutput);
	std::cerr.rdbuf(&charDebugOutput);
	std::clog.rdbuf(&charDebugOutput);
	std::ios_base::sync_with_stdio(true);
}



#endif


END_NAMESPACE
