#pragma once

#include "CoreTypes.h"
#include "Templates/Utility.h"
#include "TypeTraits/TypeTraits.h"

NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
NAMESPACE_MODULE_BEGIN(Utility)

NAMESPACE_PRIVATE_BEGIN

struct InvokeFunction
{
	template <typename F, typename... Ts>
	static auto Invoke(F&& Object, Ts&&... Args)
		-> decltype(Forward<F>(Object)(Forward<Ts>(Args)...))
	{
		return Forward<F>(Object)(Forward<Ts>(Args)...);
	}
};

struct InvokeMemberFunction
{
	template <typename F, typename ObjectType, typename... Ts>
	static auto Invoke(F&& Func, ObjectType&& Object, Ts&&... Args)
		-> decltype((Forward<ObjectType>(Object)->*Func)(Forward<Ts>(Args)...))
	{
		return (Forward<ObjectType>(Object)->*Func)(Forward<Ts>(Args)...);
	}

	template <typename F, typename ObjectType, typename... Ts>
	static auto Invoke(F&& Func, ObjectType&& Object, Ts&&... Args)
		-> decltype((Forward<ObjectType>(Object).*Func)(Forward<Ts>(Args)...))
	{
		return (Forward<ObjectType>(Object).*Func)(Forward<Ts>(Args)...);
	}
};

struct InvokeMemberObject
{
	template <typename F, typename ObjectType>
	static auto Invoke(F&& Func, ObjectType&& Object)
		-> decltype(Forward<ObjectType>(Object)->*Func)
	{
		return (Forward<ObjectType>(Object)->*Func);
	}

	template <typename F, typename ObjectType>
	static auto Invoke(F&& Func, ObjectType&& Object)
		-> decltype(Forward<ObjectType>(Object).*Func)
	{
		return (Forward<ObjectType>(Object).*Func);
	}
};

template <typename F,
	typename T,
	typename Decayed = TDecay<F>,
	bool IsMemberFunction = CMemberFunctionPointer<Decayed>,
	bool IsMemberObject = CMemberObjectPointer<Decayed>>
	struct InvokeMember;

template <typename F, typename T, typename Decayed>
struct InvokeMember<F, T, Decayed,  true, false> : InvokeMemberFunction { };

template <typename F, typename T, typename Decayed>
struct InvokeMember<F, T, Decayed, false,  true> : InvokeMemberObject { };

template <typename F, typename T, typename Decayed>
struct InvokeMember<F, T, Decayed, false, false> : InvokeFunction { };

template <typename F, typename... Ts>
struct InvokeImpl;

template <typename F>
struct InvokeImpl<F> : InvokeFunction { };

template <typename F, typename T, typename... Ts>
struct InvokeImpl<F, T, Ts...> : InvokeMember<F, T> { };

NAMESPACE_PRIVATE_END

/** Invoke the Callable object f with the parameters args. */
template <typename F, typename... Ts> requires (CInvocable<F, Ts...>)
FORCEINLINE constexpr auto Invoke(F&& Func, Ts&&... Args)
	-> decltype(NAMESPACE_PRIVATE::InvokeImpl<F, Ts...>::Invoke(Forward<F>(Func), Forward<Ts>(Args)...))
{
	return NAMESPACE_PRIVATE::InvokeImpl<F, Ts...>::Invoke(Forward<F>(Func), Forward<Ts>(Args)...);
}

/** Invoke the Callable object f with the parameters args. */
template <typename R, typename F, typename... Ts> requires (CInvocableResult<R, F, Ts...>)
NODISCARD FORCEINLINE constexpr R InvokeResult(F&& Func, Ts&&... Args)
{
	if constexpr (CVoid<R>) Invoke(Forward<F>(Func), Forward<Ts>(Args)...);
	else             return Invoke(Forward<F>(Func), Forward<Ts>(Args)...);
}

NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END