///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                           //
//  Copyright (c) 2012-2015, Jan de Graaf (jan@jlib.nl)                                                      //
//                                                                                                           //
//  Permission to use, copy, modify, and/or distribute this software for any purpose with or                 //
//  without fee is hereby granted, provided that the above copyright notice and this permission              //
//  notice appear in all copies.                                                                             //
//                                                                                                           //
//  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS             //
//  SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL              //
//  THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES          //
//  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE     //
//  OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.  //
//                                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef _JLIB_HEADER_APPEND
#define _JLIB_HEADER_APPEND

#include <string>
#include <sstream>
#include <iostream>
#include <type_traits>

namespace std
{
	// Stream operator
	template<typename char_type_t, typename input_type_t> inline basic_string<char_type_t>& operator<<(basic_string<char_type_t>& lhs, const input_type_t& rhs)
	{
		basic_ostringstream<char_type_t> stream;
		return lhs.append(static_cast<basic_ostringstream<char_type_t>*>(&(stream << rhs))->str());
	}

	// Parsing numbers
	template<typename char_type_t, typename input_type_t> inline bool operator>>(const basic_string<char_type_t>& lhs, input_type_t& rhs)
	{
		return (bool)(basic_istringstream<char_type_t>(lhs) >> rhs);
	}

	// Addition operators
	// Utilizes enable_if to prevent colliding with string's existing operators
	template<typename char_type_t, typename value_type> struct is_string_operand
	{
		static const bool value = std::is_same<typename std::decay<value_type>::type, char_type_t>::value || std::is_same<typename std::decay<value_type>::type, char_type_t*>::value || std::is_same<typename std::decay<value_type>::type, std::basic_string<char_type_t>>::value;
	};
	template<typename char_type_t, typename input_type_t> inline typename std::enable_if<!is_string_operand<char_type_t, input_type_t>::value, basic_string<char_type_t>&>::type operator+=(basic_string<char_type_t>& lhs, const input_type_t& rhs)
	{
		basic_ostringstream<char_type_t> stream;
		return lhs.append(static_cast<basic_ostringstream<char_type_t>*>(&(stream << rhs))->str());
	}
	template<typename char_type_t, typename input_type_t> inline typename std::enable_if<!is_string_operand<char_type_t, input_type_t>::value, basic_string<char_type_t>>::type operator+(const basic_string<char_type_t>& lhs, const input_type_t& rhs)
	{
		basic_ostringstream<char_type_t> stream;
		return basic_string<char_type_t>(lhs).append(static_cast<basic_ostringstream<char_type_t>*>(&(stream << rhs))->str());
	}
}

template<typename to_type_t, typename from_type_t> inline std::basic_string<to_type_t> convert_cstring_type(const from_type_t* cstr)
{
	static_assert(std::is_same<to_type_t, from_type_t>::value, "Invalid char overload");
	return std::basic_string<to_type_t>(cstr);
}
template<> inline std::basic_string<char> convert_cstring_type(const wchar_t* wcstr)
{
	std::string str;
	size_t i = 0;
	while (wcstr[i] != L'\0')
	{
		const uint32_t c = uint32_t(wcstr[i]);
		str.resize(i + 1);
		str[i++] = (c < 32 || c >= 126) ? '?' : char(c);
	}
	return str;
}
template<> inline std::basic_string<wchar_t> convert_cstring_type(const char* cstr)
{
	std::wstring wstr;
	size_t i = 0;
	while (cstr[i] != L'\0')
	{
		const wchar_t c = wchar_t(cstr[i]);
		wstr.resize(i + 1);
		wstr[i++] = c;
	}
	return wstr;
}
template<typename to_type_t, typename from_type_t> inline std::basic_string<to_type_t> convert_string_type(const std::basic_string<from_type_t>& from_string)
{
	static_assert(std::is_same<to_type_t, from_type_t>::value, "Invalid char overload");
	return from_string;
}
template<> inline std::basic_string<char> convert_string_type(const std::basic_string<wchar_t>& wstr)
{
	return convert_cstring_type<char, wchar_t>(wstr.data());
}
template<> inline std::basic_string<wchar_t> convert_string_type(const std::basic_string<char>& str)
{
	return convert_cstring_type<wchar_t, char>(str.data());
}

namespace std
{
#ifndef _NOUNREAL
	template<typename char_type_t> inline basic_string<char_type_t> operator+(const basic_string<char_type_t>& lhs, const class FString& rhs)
	{
		if (rhs.GetCharArray().GetData())
			return basic_string<char_type_t>(lhs).append((convert_cstring_type<char_type_t, wchar_t>(rhs.GetCharArray().GetData())));
		return basic_string<char_type_t>(lhs);
	}
	template<typename char_type_t> inline basic_string<char_type_t> operator+(const basic_string<char_type_t>& lhs, const class FName& rhs)
	{
		return basic_string<char_type_t>(lhs) + rhs.ToString();
	}
	template<typename char_type_t> inline basic_string<char_type_t> operator+(const basic_string<char_type_t>& lhs, const class FText& rhs)
	{
		return basic_string<char_type_t>(lhs) + rhs.ToString();
	}

	template<typename char_type_t> inline basic_string<char_type_t> operator+(const basic_string<char_type_t>& lhs, const struct FVector& rhs)
	{
		static const char __LogSpace = ' ';
		static const char __FVectorOpen = '{';
		static const char __FVectorEnd = '}';
		static const char __LogSep = ',';
		return lhs + (char_type_t)__FVectorOpen + rhs.X + (char_type_t)__LogSep + (char_type_t)__LogSpace + rhs.Y + (char_type_t)__LogSep + (char_type_t)__LogSpace + rhs.Z + (char_type_t)__FVectorEnd;
	}
	template<typename char_type_t> inline basic_string<char_type_t> operator+(const basic_string<char_type_t>& lhs, const struct FVector2D& rhs)
	{
		static const char __LogSpace = ' ';
		static const char __FVectorOpen = '{';
		static const char __FVectorEnd = '}';
		static const char __LogSep = ',';
		return lhs + (char_type_t)__FVectorOpen + rhs.X + (char_type_t)__LogSep + (char_type_t)__LogSpace + rhs.Y + (char_type_t)__FVectorEnd;
	}
	template<typename char_type_t> inline basic_string<char_type_t> operator+(const basic_string<char_type_t>& lhs, const struct FRotator& rhs)
	{
		static const char __LogSpace = ' ';
		static const char __FVectorOpen = '{';
		static const char __FVectorEnd = '}';
		static const char __LogSep = ',';
		return lhs + (char_type_t)__FVectorOpen + rhs.Pitch + (char_type_t)__LogSep + (char_type_t)__LogSpace + rhs.Yaw + (char_type_t)__LogSep + (char_type_t)__LogSpace + rhs.Roll + (char_type_t)__FVectorEnd;
	}
#endif
}

#endif