#pragma once

#include <string>
#include <string_view>
#include <functional>
#include <cstdint>

namespace psemek::util
{

	struct hstring
	{
		hstring(std::string string)
			: string_(std::move(string))
			, view_(string_)
		{}

		hstring(std::string_view const & view)
			: view_(view)
		{}

		hstring(char const * str)
			: view_(str)
		{}

		hstring(hstring const & other)
			: string_(other.string_)
			, view_(other.owns() ? std::string_view(string_) : other.view_)
		{}

		hstring(hstring && other)
		{
			if (other.owns())
			{
				string_ = std::move(other.string_);
				view_ = string_;
			}
			else
			{
				view_ = other.view_;
			}

			other.view_ = std::string_view();
		}

		std::string_view view() const
		{
			return view_;
		}

		bool owns() const
		{
			return view_.data() == string_.data();
		}

	private:
		std::string string_;
		std::string_view view_;
	};

	inline auto operator == (hstring const & str1, hstring const & str2)
	{
		return str1.view() == str2.view();
	}

	inline auto operator <=> (hstring const & str1, hstring const & str2)
	{
#ifdef __clang__
		// libc++ in MacOSX12.3.sdk doesn't have operator <=> for string views
		auto view1 = str1.view();
		auto view2 = str2.view();
		if (std::lexicographical_compare(view1.begin(), view1.end(), view2.begin(), view2.end()))
			return std::strong_ordering::less;
		else if (std::equal(view1.begin(), view1.end(), view2.begin(), view2.end()))
			return std::strong_ordering::equal;
		else
			return std::strong_ordering::greater;
#else
		return str1.view() <=> str2.view();
#endif
	}

}

namespace std
{

	template <>
	struct hash<::psemek::util::hstring>
	{
		std::uint64_t operator()(::psemek::util::hstring const & str) const
		{
			return std::hash<std::string_view>()(str.view());
		}
	};

}
