#pragma once

#include <psemek/util/range.hpp>

#include <optional>
#include <tuple>

namespace psemek::util
{

	namespace detail
	{

		template <typename F>
		struct fmap
		{
			F f;

			template <typename T>
			auto operator()(std::optional<T> && x)
			{
				using R = decltype(f(*x));

				if (x)
					return std::optional<R>(f(*x));
				else
					return std::optional<R>();
			}

			template <typename ... Ts>
			auto operator()(std::tuple<Ts...> && x)
			{
				return [&]<std::size_t ... I>(std::index_sequence<I...>){
					return std::tuple{
						f(std::get<I>(x))...
					};
				}(std::make_index_sequence<sizeof...(Ts)>{});
			}
		};

	}

	template <typename F>
	auto fmap(F f)
	{
		return detail::fmap<F>{std::move(f)};
	}

}
