#include <std_include.hpp>

#include "string.hpp"
#include <sstream>
#include <cstdarg>
#include <algorithm>

namespace utils
{
	namespace string
	{
		const char* va(const char* fmt, ...)
		{
			static thread_local va_provider<8, 256> provider;

			va_list ap;
			va_start(ap, fmt);

			const char* result = provider.get(fmt, ap);

			va_end(ap);
			return result;
		}

		std::vector<std::string> split(const std::string& s, const char delim)
		{
			std::stringstream ss(s);
			std::string item;
			std::vector<std::string> elems;

			while (std::getline(ss, item, delim))
			{
				elems.push_back(item);
				item = std::string();
			}

			return elems;
		}

		std::string to_lower(std::string text)
		{
			std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input)
			{
				return static_cast<char>(std::tolower(input));
			});

			return text;
		}

		std::string to_upper(std::string text)
		{
			std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input)
			{
				return static_cast<char>(std::toupper(input));
			});

			return text;
		}

		bool starts_with(const std::string& text, const std::string& substring)
		{
			return text.find(substring) == 0;
		}

		bool ends_with(const std::string& text, const std::string& substring)
		{
			if (substring.size() > text.size()) return false;
			return std::equal(substring.rbegin(), substring.rend(), text.rbegin());
		}

		std::string replace(std::string str, const std::string& from, const std::string& to)
		{
			if (from.empty())
			{
				return str;
			}

			std::size_t start_pos = 0;
			while ((start_pos = str.find(from, start_pos)) != std::string::npos)
			{
				str.replace(start_pos, from.length(), to);
				start_pos += to.length();
			}

			return str;
		}

		std::string dump_hex(const std::string& data, const std::string& separator)
		{
			std::string result;

			for (unsigned int i = 0; i < data.size(); ++i)
			{
				if (i > 0)
				{
					result.append(separator);
				}

				result.append(va("%02X", data[i] & 0xFF));
			}

			return result;
		}
	}
}
