/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 * Copyright (C) 2006-2014  Georg Rudoy
 *
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 **********************************************************************/

#pragma once

#include <type_traits>
#include <utility>

namespace LeechCraft
{
namespace Util
{
	template<typename F, typename... Args>
	auto Invoke (F&& f, Args&&... args) -> decltype (std::forward<F> (f) (std::forward<Args> (args)...))
	{
		return std::forward<F> (f) (std::forward<Args> (args)...);
	}

	template<typename Base, typename Real, typename Res>
	auto Invoke (Res Base::* mem, Real&& obj,
			typename std::enable_if<!std::is_member_function_pointer<decltype (mem)>::value>::type* = nullptr) -> decltype (obj.*mem)
	{
		return obj.*mem;
	}

	template<typename Base, typename Real, typename Res, typename... Rest>
	auto Invoke (Res Base::* mem, Real&& obj, Rest&&... rest) -> decltype ((obj.*mem) (std::forward<Rest> (rest)...))
	{
		return (obj.*mem) (std::forward<Rest> (rest)...);
	}

	template<typename Base, typename Real, typename Res, typename... Rest>
	auto Invoke (Res Base::* mem, Real&& obj, Rest&&... rest) -> decltype ((obj->*mem) (std::forward<Rest> (rest)...))
	{
		return (obj->*mem) (std::forward<Rest> (rest)...);
	}

	namespace CPP17
	{
		namespace detail
		{
			template<typename F, typename Tuple, size_t... Is>
			auto ApplyImpl (F&& f, Tuple&& t, std::index_sequence<Is...>)
			{
				return Invoke (std::forward<F> (f), std::get<Is> (std::forward<Tuple> (t))...);
			}
		}

		template<typename F, typename Tuple>
		auto Apply (F&& f, Tuple&& tuple)
		{
			return detail::ApplyImpl (std::forward<F> (f), std::forward<Tuple> (tuple),
					std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>> {}> {});
		}
	}
}
}
