// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - Core module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Core/TypeList.hpp>
#include <type_traits>
#include <Nazara/Core/Debug.hpp>

namespace Nz::Detail
{
	template<typename NewType, typename... ListTypes>
	struct ListAppend<TypeList<ListTypes...>, NewType>
	{
		using Result = TypeList<ListTypes..., NewType>;
	};


	template<typename T, typename... ListTypes>
	struct ListAt<TypeList<T, ListTypes...>, 0>
	{
		using Type = T;
	};

	template<std::size_t Index, typename T, typename... ListTypes>
	struct ListAt<TypeList<T, ListTypes...>, Index>
	{
		static_assert(Index <= sizeof...(ListTypes), "Index out of range");

		using Type = typename ListAt<TypeList<ListTypes...>, Index - 1>::Type;
	};


	template<typename... First>
	struct ListConcat<TypeList<First...>, TypeList<>>
	{
		using Result = TypeList<First...>;
	};

	template<typename... Second>
	struct ListConcat<TypeList<>, TypeList<Second...>>
	{
		using Result = TypeList<Second...>;
	};

	template<typename... First, typename T>
	struct ListConcat<TypeList<First...>, TypeList<T>>
	{
		using Result = TypeList<First..., T>;
	};

	template<typename... First, typename T1, typename T2, typename... Second>
	struct ListConcat<TypeList<First...>, TypeList<T1, T2, Second...>>
	{
		using Result = typename ListConcat<TypeList<First..., T1>, TypeList<T2, Second...>>::Result;
	};


	struct ListFindHelper
	{
		template<typename ToFind, typename Type, typename... Rest> static constexpr bool Find()
		{
			if constexpr (std::is_same_v<ToFind, Type>)
				return true;
			else
				return Find<ToFind, Rest...>();
		}

		template<typename ToFind> static constexpr bool Find()
		{
			return false;
		}
	};

	template<typename TypeToFind, typename... ListTypes>
	struct ListFind<TypeList<ListTypes...>, TypeToFind>
	{
		static constexpr bool Find()
		{
			return ListFindHelper::Find<TypeToFind, ListTypes...>();
		}
	};


	template<template<typename...> typename Class, typename... ListTypes>
	struct ListInstantiate<TypeList<ListTypes...>, Class>
	{
		using Result = Class<ListTypes...>;
	};


	template<typename NewType, typename... ListTypes>
	struct ListPrepend<TypeList<ListTypes...>, NewType>
	{
		using Result = TypeList<NewType, ListTypes...>;
	};


	template<typename... Types, typename T1>
	struct ListUnique<TypeList<Types...>, TypeList<T1>>
	{
		static constexpr bool IsTypePresent = ListFind<TypeList<Types...>, T1>::Find();
		using Result = std::conditional_t<!IsTypePresent, TypeList<Types..., T1>, TypeList<Types...>>;
	};

	template<typename... Types, typename T1, typename T2, typename... Rest>
	struct ListUnique<TypeList<Types...>, TypeList<T1, T2, Rest...>>
	{
		using Result = typename ListUnique<typename ListUnique<TypeList<Types...>, TypeList<T1>>::Result, TypeList<T2, Rest...>>::Result;
	};
}

#include <Nazara/Core/DebugOff.hpp>
