/*
	This file is part of Fennix Kernel.

	Fennix Kernel is free software: you can redistribute it and/or
	modify it under the terms of the GNU General Public License as
	published by the Free Software Foundation, either version 3 of
	the License, or (at your option) any later version.

	Fennix Kernel is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once

namespace std
{
	template <class T>
	struct add_cv
	{
		typedef const volatile T type;
	};

	template <class T>
	struct add_const
	{
		typedef const T type;
	};

	template <class T>
	struct add_volatile
	{
		typedef volatile T type;
	};

	template <class T>
	using add_cv_t = typename add_cv<T>::type;

	template <class T>
	using add_const_t = typename add_const<T>::type;

	template <class T>
	using add_volatile_t = typename add_volatile<T>::type;

	template <typename T>
	struct is_trivially_copyable
	{
		static constexpr bool value = __is_trivially_copyable(T);
	};

	template <class T>
	struct remove_reference
	{
		typedef T type;
	};

	template <class T>
	struct remove_reference<T &>
	{
		typedef T type;
	};

	template <class T>
	struct remove_reference<T &&>
	{
		typedef T type;
	};

	template <class T>
	using remove_reference_t = typename remove_reference<T>::type;

	template <class T, T v>
	struct integral_constant
	{
		static constexpr T value = v;
		typedef T value_type;
		typedef integral_constant type;
		constexpr operator value_type() const noexcept { return value; }
		constexpr value_type operator()() const noexcept { return value; }
	};

	template <class T>
	struct is_array : public integral_constant<bool, false>
	{
	};

	template <class T>
	struct is_array<T[]> : public integral_constant<bool, true>
	{
	};

	template <class T, __SIZE_TYPE__ N>
	struct is_array<T[N]> : public integral_constant<bool, true>
	{
	};

	template <class T>
	inline constexpr bool is_array_v = is_array<T>::value;

	template <bool B, class T, class F>
	struct conditional
	{
		typedef T type;
	};

	template <class T, class F>
	struct conditional<false, T, F>
	{
		typedef F type;
	};

	template <bool B, class T, class F>
	using conditional_t = typename conditional<B, T, F>::type;

	template <class T>
	struct type_identity
	{
		using type = T;
	};

	template <class T>
	using type_identity_t = type_identity<T>::type;

	template <class T>
	auto __try_add_pointer(int) -> type_identity<typename std::remove_reference<T>::type *>;

	template <class T>
	auto __try_add_pointer(...) -> type_identity<T>;

	template <class T>
	struct add_pointer : decltype(__try_add_pointer<T>(0))
	{
	};

	template <class T>
	using add_pointer_t = typename add_pointer<T>::type;

	template <class T>
	struct remove_extent
	{
		using type = T;
	};

	template <class T>
	struct remove_extent<T[]>
	{
		using type = T;
	};

	template <class T, __SIZE_TYPE__ N>
	struct remove_extent<T[N]>
	{
		using type = T;
	};

	template <class T>
	using remove_extent_t = typename remove_extent<T>::type;

	template <class T>
	struct remove_cv
	{
		typedef T type;
	};
	template <class T>
	struct remove_cv<const T>
	{
		typedef T type;
	};
	template <class T>
	struct remove_cv<volatile T>
	{
		typedef T type;
	};
	template <class T>
	struct remove_cv<const volatile T>
	{
		typedef T type;
	};

	template <class T>
	struct remove_const
	{
		typedef T type;
	};
	template <class T>
	struct remove_const<const T>
	{
		typedef T type;
	};

	template <class T>
	struct remove_volatile
	{
		typedef T type;
	};
	template <class T>
	struct remove_volatile<volatile T>
	{
		typedef T type;
	};

	template <class T>
	using remove_cv_t = typename remove_cv<T>::type;

	template <class T>
	using remove_const_t = typename remove_const<T>::type;

	template <class T>
	using remove_volatile_t = typename remove_volatile<T>::type;

	template <class T>
	struct is_lvalue_reference : public integral_constant<bool, false>
	{
	};

	template <class T>
	struct is_lvalue_reference<T &> : public integral_constant<bool, true>
	{
	};

	template <class T>
	inline constexpr bool is_lvalue_reference_v = is_lvalue_reference<T>::value;

	template <class T>
	struct is_const : public integral_constant<bool, false>
	{
	};
	template <class T>
	struct is_const<const T> : public integral_constant<bool, true>
	{
	};

	template <class T>
	inline constexpr bool is_const_v = is_const<T>::value;

	template <class T>
	struct is_reference : public integral_constant<bool, false>
	{
	};

	template <class T>
	struct is_reference<T &> : public integral_constant<bool, true>
	{
	};

	template <class T>
	struct is_reference<T &&> : public integral_constant<bool, true>
	{
	};

	template <class T>
	inline constexpr bool is_reference_v = is_reference<T>::value;

	template <class T>
	struct is_function : std::integral_constant<
							 bool,
							 !std::is_const<const T>::value && !std::is_reference<T>::value>
	{
	};

	template <class T>
	struct decay
	{
	private:
		typedef typename std::remove_reference<T>::type U;

	public:
		typedef typename std::conditional<
			std::is_array<U>::value,
			typename std::add_pointer<typename std::remove_extent<U>::type>::type,
			typename std::conditional<
				std::is_function<U>::value,
				typename std::add_pointer<U>::type,
				typename std::remove_cv<U>::type>::type>::type type;
	};

	template <class T>
	using decay_t = typename decay<T>::type;

	template <bool B>
	using bool_constant = integral_constant<bool, B>;

	template <class T>
		struct is_integral : bool_constant < requires(T t, T *p, void (*f)(T))
	{
		reinterpret_cast<T>(t);
		f(0);
		p + t;
	}>{};

	template <class T>
	constexpr bool is_integral_v = is_integral<T>::value;

	typedef integral_constant<bool, true> true_type;
	typedef integral_constant<bool, false> false_type;

	template <bool, class T = void>
	struct enable_if
	{
	};

	template <class T>
	struct enable_if<true, T>
	{
		typedef T type;
	};

	template <bool B, class T = void>
	using enable_if_t = typename enable_if<B, T>::type;

	template <class T>
	auto __try_add_lvalue_reference(int) -> type_identity<T &>;

	template <class T>
	auto __try_add_lvalue_reference(...) -> type_identity<T>;

	template <class T>
	auto __try_add_rvalue_reference(int) -> type_identity<T &&>;

	template <class T>
	auto __try_add_rvalue_reference(...) -> type_identity<T>;

	template <class T>
	struct add_lvalue_reference : decltype(__try_add_lvalue_reference<T>(0))
	{
	};

	template <class T>
	struct add_rvalue_reference : decltype(__try_add_rvalue_reference<T>(0))
	{
	};

	template <class T>
	using add_lvalue_reference_t = typename add_lvalue_reference<T>::type;

	template <class T>
	using add_rvalue_reference_t = typename add_rvalue_reference<T>::type;

	template <typename T>
	typename std::add_rvalue_reference<T>::type declval() noexcept
	{
		static_assert(false, "declval not allowed in an evaluated context");
	}

	template <class T, class... Args>
	struct is_constructible : public integral_constant<bool, __is_constructible(T, Args...)>
	{
	};

	template <class T, class... Args>
	struct is_trivially_constructible : public integral_constant<bool, __is_trivially_constructible(T, Args...)>
	{
	};

	template <class T, class... Args>
	struct is_nothrow_constructible : public integral_constant<bool, __is_nothrow_constructible(T, Args...)>
	{
	};

	template <class T, class... Args>
	inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value;

	template <class T, class... Args>
	inline constexpr bool is_trivially_constructible_v = is_trivially_constructible<T, Args...>::value;

	template <class T, class... Args>
	inline constexpr bool is_nothrow_constructible_v = is_nothrow_constructible<T, Args...>::value;

	template <class T>
	struct is_default_constructible : std::is_constructible<T>
	{
	};

	template <class T>
	struct is_trivially_default_constructible : std::is_trivially_constructible<T>
	{
	};

	template <class T>
	struct is_nothrow_default_constructible : std::is_nothrow_constructible<T>
	{
	};

	template <class T>
	inline constexpr bool is_default_constructible_v = is_default_constructible<T>::value;

	template <class T>
	inline constexpr bool is_trivially_default_constructible_v = is_trivially_default_constructible<T>::value;

	template <class T>
	inline constexpr bool is_nothrow_default_constructible_v = is_nothrow_default_constructible<T>::value;

	template <class T, class U>
	struct is_assignable : public integral_constant<bool, __is_assignable(T, U)>
	{
	};

	template <class T, class U>
	struct is_trivially_assignable : public integral_constant<bool, __is_trivially_assignable(T, U)>
	{
	};

	template <class T, class U>
	struct is_nothrow_assignable : public integral_constant<bool, __is_nothrow_assignable(T, U)>
	{
	};

	template <class T, class U>
	inline constexpr bool is_assignable_v = is_assignable<T, U>::value;

	template <class T, class U>
	inline constexpr bool is_trivially_assignable_v = is_trivially_assignable<T, U>::value;

	template <class T, class U>
	inline constexpr bool is_nothrow_assignable_v = is_nothrow_assignable<T, U>::value;

	template <class T>
	struct is_move_assignable : std::is_assignable<typename std::add_lvalue_reference<T>::type, typename std::add_rvalue_reference<T>::type>
	{
	};

	template <class T>
	struct is_trivially_move_assignable : std::is_trivially_assignable<typename std::add_lvalue_reference<T>::type, typename std::add_rvalue_reference<T>::type>
	{
	};

	template <class T>
	struct is_nothrow_move_assignable : std::is_nothrow_assignable<typename std::add_lvalue_reference<T>::type, typename std::add_rvalue_reference<T>::type>
	{
	};

	template <class T>
	inline constexpr bool is_move_assignable_v = is_move_assignable<T>::value;

	template <class T>
	inline constexpr bool is_trivially_move_assignable_v = is_trivially_move_assignable<T>::value;

	template <class T>
	inline constexpr bool is_nothrow_move_assignable_v = is_nothrow_move_assignable<T>::value;

	template <class T>
	struct is_move_constructible : std::is_constructible<T, typename std::add_rvalue_reference<T>::type>
	{
	};

	template <class T>
	struct is_trivially_move_constructible : std::is_trivially_constructible<T, typename std::add_rvalue_reference<T>::type>
	{
	};

	template <class T>
	struct is_nothrow_move_constructible : std::is_nothrow_constructible<T, typename std::add_rvalue_reference<T>::type>
	{
	};

	template <class T>
	inline constexpr bool is_move_constructible_v = is_move_constructible<T>::value;

	template <class T>
	inline constexpr bool is_trivially_move_constructible_v = is_trivially_move_constructible<T>::value;

	template <class T>
	inline constexpr bool is_nothrow_move_constructible_v = is_nothrow_move_constructible<T>::value;

	template <typename T>
	typename std::remove_reference<T>::type &&__type_traits_move(T &&arg)
	{
		return static_cast<typename std::remove_reference<T>::type &&>(arg);
	}

	template <typename T>
	void __type_traits_swap(T &a, T &b)
	{
		T temp = __type_traits_move(a);
		a = __type_traits_move(b);
		b = __type_traits_move(temp);
	}

	template <class T, class U>
	struct is_swappable_with
	{
		template <class X, class Y,
				  typename = decltype(__type_traits_swap(std::declval<X>(), std::declval<Y>())),
				  typename = decltype(__type_traits_swap(std::declval<Y>(), std::declval<X>()))>
		static std::true_type test(int);

		template <class, class>
		static std::false_type test(...);

		static constexpr bool value = decltype(test<T, U>(0))::value;
	};

	template <class T>
	struct is_swappable
	{
		static constexpr bool value = is_swappable_with<T, T>::value;
	};

	template <class T, class U>
	struct is_nothrow_swappable_with
	{
		template <class X, class Y,
				  typename = decltype(__type_traits_swap(std::declval<X>(), std::declval<Y>())),
				  typename = decltype(__type_traits_swap(std::declval<Y>(), std::declval<X>()))>
		static std::true_type test(int);

		template <class, class>
		static std::false_type test(...);

		static constexpr bool value = decltype(test<T, U>(0))::value;
	};

	template <class T>
	struct is_nothrow_swappable
	{
		static constexpr bool value = is_nothrow_swappable_with<T, T>::value;
	};

	template <class T, class U>
	inline constexpr bool is_swappable_with_v = is_swappable_with<T, U>::value;

	template <class T>
	inline constexpr bool is_swappable_v = is_swappable<T>::value;

	template <class T, class U>
	inline constexpr bool is_nothrow_swappable_with_v = is_nothrow_swappable_with<T, U>::value;

	template <class T>
	inline constexpr bool is_nothrow_swappable_v = is_nothrow_swappable<T>::value;

	template <class T>
	struct is_copy_constructible : std::is_constructible<T, typename std::add_lvalue_reference<typename std::add_const<T>::type>::type>
	{
	};

	template <class T>
	struct is_trivially_copy_constructible : std::is_trivially_constructible<T, typename std::add_lvalue_reference<typename std::add_const<T>::type>::type>
	{
	};

	template <class T>
	struct is_nothrow_copy_constructible : std::is_nothrow_constructible<T, typename std::add_lvalue_reference<typename std::add_const<T>::type>::type>
	{
	};

	template <class T>
	inline constexpr bool is_copy_constructible_v = is_copy_constructible<T>::value;

	template <class T>
	inline constexpr bool is_trivially_copy_constructible_v = is_trivially_copy_constructible<T>::value;

	template <class T>
	inline constexpr bool is_nothrow_copy_constructible_v = is_nothrow_copy_constructible<T>::value;

	template <class T, class U>
	struct is_same : std::false_type
	{
	};

	template <class T>
	struct is_same<T, T> : std::true_type
	{
	};

	template <class T, class U>
	inline constexpr bool is_same_v = is_same<T, U>::value;

	template <class T>
	struct is_unbounded_array : std::false_type
	{
	};

	template <class T>
	struct is_unbounded_array<T[]> : std::true_type
	{
	};

	template <class T>
	inline constexpr bool is_unbounded_array_v = is_unbounded_array<T>::value;

	template <class T>
	struct is_bounded_array : std::false_type
	{
	};

	template <class T, __SIZE_TYPE__ N>
	struct is_bounded_array<T[N]> : std::true_type
	{
	};

	template <class T>
	inline constexpr bool is_bounded_array_v = is_bounded_array<T>::value;

	template <class...>
	struct common_type
	{
	};

	template <class T>
	struct common_type<T> : common_type<T, T>
	{
	};

	namespace __type_traits_detail
	{
		template <class...>
		using void_t = void;

		template <class T1, class T2>
		using conditional_result_t = decltype(false ? std::declval<T1>() : std::declval<T2>());

		template <class, class, class = void>
		struct decay_conditional_result
		{
		};

		template <class T1, class T2>
		struct decay_conditional_result<T1, T2, void_t<conditional_result_t<T1, T2>>> : std::decay<conditional_result_t<T1, T2>>
		{
		};

		template <class T1, class T2, class = void>
		struct common_type_2_impl : decay_conditional_result<const T1 &, const T2 &>
		{
		};

		template <class T1, class T2>
		struct common_type_2_impl<T1, T2, void_t<conditional_result_t<T1, T2>>> : decay_conditional_result<T1, T2>
		{
		};

		template <class AlwaysVoid, class T1, class T2, class... R>
		struct common_type_multi_impl
		{
		};

		template <class T1, class T2, class... R>
		struct common_type_multi_impl<void_t<typename common_type<T1, T2>::type>, T1, T2, R...> : common_type<typename common_type<T1, T2>::type, R...>
		{
		};
	}

	template <class T1, class T2>
	struct common_type<T1, T2> : std::conditional<std::is_same<T1, typename std::decay<T1>::type>::value &&
													  std::is_same<T2, typename std::decay<T2>::type>::value,
												  __type_traits_detail::common_type_2_impl<T1, T2>,
												  common_type<typename std::decay<T1>::type, typename std::decay<T2>::type>>::type
	{
	};

	template <class T1, class T2, class... R>
	struct common_type<T1, T2, R...> : __type_traits_detail::common_type_multi_impl<void, T1, T2, R...>
	{
	};

	template <class... T>
	using common_type_t = typename common_type<T...>::type;

	namespace detail
	{
		template <class T>
		constexpr T &&__result_of_forward(std::remove_reference_t<T> &t) noexcept
		{
			return static_cast<T &&>(t);
		}

		template <class F, class... ArgTypes>
		auto INVOKE(F &&f, ArgTypes &&...args)
			-> decltype(__result_of_forward<F>(f)(__result_of_forward<ArgTypes>(args)...))
		{
			return __result_of_forward<F>(f)(__result_of_forward<ArgTypes>(args)...);
		}
	}

	template <class>
	class result_of;

	template <class F, class... ArgTypes>
	struct result_of<F(ArgTypes...)>
	{
		using type = decltype(detail::INVOKE(std::declval<F>(), std::declval<ArgTypes>()...));
	};

	template <class F, class... ArgTypes>
	struct invoke_result;

	template <class T>
	using result_of_t = typename result_of<T>::type;

	template <class F, class... ArgTypes>
	using invoke_result_t = typename invoke_result<F, ArgTypes...>::type;

	template <class T>
	struct is_pointer : std::false_type
	{
	};

	template <class T>
	struct is_pointer<T *> : std::true_type
	{
	};

	template <class T>
	struct is_pointer<T *const> : std::true_type
	{
	};

	template <class T>
	struct is_pointer<T *volatile> : std::true_type
	{
	};

	template <class T>
	struct is_pointer<T *const volatile> : std::true_type
	{
	};

	template <class T>
	constexpr bool is_pointer_v = is_pointer<T>::value;

	template <class T>
	struct is_floating_point : std::integral_constant<bool, std::is_same<float, typename std::remove_cv<T>::type>::value || std::is_same<double, typename std::remove_cv<T>::type>::value || std::is_same<long double, typename std::remove_cv<T>::type>::value>
	{
	};

	template <class T>
	constexpr bool is_floating_point_v = is_floating_point<T>::value;
}
