#pragma once

#include <psemek/util/exception.hpp>

#include <sstream>
#include <stdexcept>
#include <vector>

namespace psemek::util
{

	namespace detail
	{

		template <typename Char, typename Traits = std::char_traits<Char>, typename ... Args>
		std::basic_string<Char, Traits> to_string(Args const & ... args)
		{
			std::basic_ostringstream<Char, Traits> oss;

			((oss << args), ...);

			return oss.str();
		}

		template <typename Char, typename Traits = std::char_traits<Char>>
		struct to_string_impl
		{
			template <typename ... Args>
			std::basic_string<Char, Traits> operator()(Args const & ... args) const
			{
				return detail::to_string<Char, Traits>(args...);
			}
		};

	}

	constexpr detail::to_string_impl<char> to_string;
	constexpr detail::to_string_impl<wchar_t> to_wstring;
	constexpr detail::to_string_impl<char32_t> to_u32string;

	template <typename T, typename Char, typename Traits>
	T from_string(std::basic_string<Char, Traits> const & s)
	{
		std::basic_istringstream<Char, Traits> iss(s);
		T x;
		iss >> x;
		if (!iss)
			throw exception("Failed to parse from string");
		return x;
	}

	template <typename T, typename Char>
	T from_string(Char const * s)
	{
		return from_string<T, Char, std::char_traits<Char>>(s);
	}

	inline std::vector<std::string> split(std::string const & str, char delim)
	{
		std::vector<std::string> result;

		for (std::size_t pos = 0;;)
		{
			auto next = str.find(delim, pos);

			if (next == std::string::npos)
			{
				result.push_back(str.substr(pos));
				break;
			}
			else
			{
				result.push_back(str.substr(pos, next - pos));
				pos = next + 1;
			}
		}

		return result;
	}

}
