#pragma once

#include "CoreTypes.h"
#include "Memory/Memory.h"
#include "Templates/Atomic.h"
#include "Templates/Invoke.h"
#include "Templates/Utility.h"
#include "Templates/TypeHash.h"
#include "Memory/PointerTraits.h"
#include "Memory/UniquePointer.h"
#include "Memory/MemoryOperator.h"
#include "Templates/Noncopyable.h"
#include "TypeTraits/TypeTraits.h"
#include "Miscellaneous/Compare.h"

NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
NAMESPACE_MODULE_BEGIN(Utility)

template <typename T> requires (CClass<T>)
class TSharedFromThis;

template <typename T> requires (CObject<T> && !CBoundedArray<T>)
class TSharedRef;

template <typename T> requires (CObject<T> && !CBoundedArray<T>)
class TSharedPtr;

template <typename T> requires (CObject<T> && !CBoundedArray<T>)
class TWeakPtr;

NAMESPACE_PRIVATE_BEGIN

template <typename T> struct TIsTSharedRef                : FFalse { };
template <typename T> struct TIsTSharedRef<TSharedRef<T>> : FTrue  { };

template <typename T> struct TIsTSharedPtr                : FFalse { };
template <typename T> struct TIsTSharedPtr<TSharedPtr<T>> : FTrue  { };

template <typename T> struct TIsTWeakPtr              : FFalse { };
template <typename T> struct TIsTWeakPtr<TWeakPtr<T>> : FTrue  { };

NAMESPACE_PRIVATE_END

template <typename T>
concept CTSharedRef = NAMESPACE_PRIVATE::TIsTSharedRef<TRemoveCV<T>>::Value;

template <typename T>
concept CTSharedPtr = NAMESPACE_PRIVATE::TIsTSharedPtr<TRemoveCV<T>>::Value;

template <typename T>
concept CTWeakPtr = NAMESPACE_PRIVATE::TIsTWeakPtr<TRemoveCV<T>>::Value;

NAMESPACE_PRIVATE_BEGIN

// This is the base object for TSharedPtr and uses constructive interference alignment for performance.
class alignas(Memory::ConstructiveInterference) FSharedController : private FSingleton
{
private:

	using RefCounter = TAtomic<size_t>;

	// Ensure that counters are lock-free for performance.
	static_assert(RefCounter::bIsAlwaysLockFree);

	// When this count is zero the object is destroyed.
	// This count is the number of TSharedRef and TSharedPtr.
	RefCounter SharedReferenceCount;

	// When this count is zero the controller is destroyed.
	// If SharedCounter is not zero this count is one more than the number of TWeakPtr.
	RefCounter WeakReferenceCount;

public:

	// The initialization count is one because TSharedPtr already existed when this controller was constructed.
	FORCEINLINE FSharedController() : SharedReferenceCount(1) , WeakReferenceCount(1) { }

	// The controller is a polymorphic class in order to customize the type of erasure of the deleter.
	virtual ~FSharedController() { }

	// Destructor object.
	virtual void DestroyObject() = 0;

	// Destructor this controller.
	virtual void DestroyThis() { delete this; }

	// Get shared reference count, no definite operation order.
	FORCEINLINE RefCounter::ValueType GetSharedReferenceCount()
	{
		// Get the shared reference count as EMemoryOrder::Relaxed,
		// since this count is for reference only and has no guarantees,
		// where EMemoryOrder::Relaxed only determines the atomicity of this operation and not the order.
		return SharedReferenceCount.Load(EMemoryOrder::Relaxed);
	}

	// Increases the shared reference count, ensuring that the shared reference count is non-zero before calling.
	FORCEINLINE void AddSharedReference()
	{
		// The check was removed in the release version, so you can use the default EMemoryOrder.
		check(SharedReferenceCount.Load() != 0);

		// We assume a non-zero reference count, which can be incremented directly with EMemoryOrder::Relaxed,
		// where EMemoryOrder::Relaxed only determines the atomicity of this operation and not the order.
		SharedReferenceCount.FetchAdd(1, EMemoryOrder::Relaxed);
	}

	// increment the shared reference count, do not need to ensure that the shared reference count is zero,
	// if the shared reference count is zero return false.
	bool AddSharedReferenceIfUnexpired()
	{
		RefCounter::ValueType OldSharedReferenceCount = GetSharedReferenceCount();

		// We need to make sure we don't increase the reference count from zero to one.
		while (true)
		{
			// Never add a shared reference if the pointer has already expired.
			if (OldSharedReferenceCount == 0) return false;

			// Attempt to increment the reference count.
			// We do a weak read here because we require a loop where the loop only happens in very unusual cases.
			if (SharedReferenceCount.CompareExchange(OldSharedReferenceCount, OldSharedReferenceCount + 1, EMemoryOrder::Relaxed, true)) return true;
		}
	}

	// Release the shared reference count, make sure the shared reference count is not zero before,
	// and destroy the object when the shared reference count is released to zero.
	void ReleaseSharedReference()
	{
		// Decrement with EMemoryOrder::Release and get the old value,
		// where EMemoryOrder::Release ensures that the side effects of all operations
		// on the shared reference count of all threads are visible to this thread,
		// preventing the shared reference count from actually going to zero.
		RefCounter::ValueType OldSharedReferenceCount = SharedReferenceCount.FetchSub(1, EMemoryOrder::Release);

		// Make sure the shared reference count is not zero before.
		check(OldSharedReferenceCount != 0);

		// Destroy the object when the reference count is released to zero.
		if (OldSharedReferenceCount == 1)
		{
			// Use EMemoryOrder::Acquire to ensure visibility of the side effects of the decrement to any other threads.
			AtomicThreadFence(EMemoryOrder::Acquire);

			// Destroy objects using the type-erase deleter.
			DestroyObject();

			// Release a weak reference count to indicate that no TSharedRef and TSharedPtr are referencing this controller.
			ReleaseWeakReference();
		}
	}

	// Increases the weak reference count, ensuring that the weak reference count is non-zero before calling.
	FORCEINLINE void AddWeakReference()
	{
		// The use of EMemoryOrder is the same as in AddSharedReference().

		check(WeakReferenceCount.Load() != 0);

		WeakReferenceCount.FetchAdd(1, EMemoryOrder::Relaxed);
	}

	// Release the weak reference count, make sure the weak reference count is not zero before,
	// and destroy the controller when the weak reference count is released to zero.
	void ReleaseWeakReference()
	{
		// The use of EMemoryOrder is the same as in ReleaseSharedReference().

		RefCounter::ValueType OldWeakReferenceCount = WeakReferenceCount.FetchSub(1, EMemoryOrder::Release);

		check(OldWeakReferenceCount != 0);

		if (OldWeakReferenceCount == 1)
		{
			AtomicThreadFence(EMemoryOrder::Acquire);
			DestroyThis(); // Destroy this controller.
		}
	}

};

template <typename T, typename E, bool = CEmpty<E> && !CFinal<E>>
class TSharedControllerWithDeleter;

template <typename T, typename E>
class TSharedControllerWithDeleter<T, E, true> final : public FSharedController, private E
{
public:

	TSharedControllerWithDeleter() = delete;

	FORCEINLINE TSharedControllerWithDeleter(T* InPtr) : E(), Pointer(InPtr) { }

	template<typename U>
	FORCEINLINE TSharedControllerWithDeleter(T* InPtr, U&& InDeleter) : E(Forward<U>(InDeleter)), Pointer(InPtr) { }

	virtual ~TSharedControllerWithDeleter() = default;

	virtual void DestroyObject() final { Invoke(GetDeleter(), Pointer); }

	FORCEINLINE T* GetPointer() { return Pointer; }
	FORCEINLINE E& GetDeleter() { return *this;   }

private:

	// NOTE: NO_UNIQUE_ADDRESS is not valid in MSVC, use base class instead of member variable
	//NO_UNIQUE_ADDRESS E Deleter;

	T* Pointer;

};

template <typename T, typename E>
class TSharedControllerWithDeleter<T, E, false> final : public FSharedController
{
public:

	TSharedControllerWithDeleter() = delete;

	FORCEINLINE TSharedControllerWithDeleter(T* InPtr) : Pointer(InPtr), Deleter() { }

	template<typename U>
	FORCEINLINE TSharedControllerWithDeleter(T* InPtr, U&& InDeleter) : Pointer(InPtr), Deleter(Forward<U>(InDeleter)) { }

	virtual ~TSharedControllerWithDeleter() = default;

	virtual void DestroyObject() final { Invoke(GetDeleter(), Pointer); }

	FORCEINLINE T* GetPointer() { return Pointer; }
	FORCEINLINE E& GetDeleter() { return Deleter; }

private:

	T* Pointer;
	E  Deleter;

};

template <typename T>
class TSharedControllerWithObject final : public FSharedController
{
public:

	FORCEINLINE explicit TSharedControllerWithObject(FNoInit) requires (!CConstructibleFrom<T, FNoInit>) { new (&Storage) T; }

	template <typename... Ts> requires (CConstructibleFrom<T, Ts...>)
	FORCEINLINE explicit TSharedControllerWithObject(Ts&&... Args) { new (&Storage) T(Forward<Ts>(Args)...); }

	virtual ~TSharedControllerWithObject() = default;

	virtual void DestroyObject() final { GetPointer()->~T(); }

	FORCEINLINE T* GetPointer() const { return reinterpret_cast<T*>(&Storage); }

private:

	mutable TAlignedStorage<sizeof(T), alignof(T)> Storage;

};

template <typename T>
class TSharedControllerWithArray final : public FSharedController
{
public:

	static TSharedControllerWithArray* New(size_t N, FNoInit)
	{
		void* Buffer = Memory::Malloc(sizeof(TSharedControllerWithArray) + sizeof(T) * (N - 1), alignof(TSharedControllerWithArray));
		const auto Controller = new (Buffer) TSharedControllerWithArray(N);
		const T* ElementPtr = new (Controller->GetPointer()) T[N];
		check(ElementPtr == Controller->GetPointer());
		return Controller;
	}

	static TSharedControllerWithArray* New(size_t N)
	{
		void* Buffer = Memory::Malloc(sizeof(TSharedControllerWithArray) + sizeof(T) * (N - 1), alignof(TSharedControllerWithArray));
		const auto Controller = new (Buffer) TSharedControllerWithArray(N);
		const T* ElementPtr = new (Controller->GetPointer()) T[N]();
		check(ElementPtr == Controller->GetPointer());
		return Controller;
	}

	virtual ~TSharedControllerWithArray() = default;

	virtual void DestroyObject() final { Memory::Destruct(GetPointer(), Num); }

	virtual void DestroyThis() final
	{
		this->~TSharedControllerWithArray();
		Memory::Free(this);
	}

	FORCEINLINE T* GetPointer() const { return reinterpret_cast<T*>(&Storage); }

private:

	size_t Num;

	mutable TAlignedStorage<sizeof(T), alignof(T)> Storage;

	FORCEINLINE explicit TSharedControllerWithArray(size_t N) : Num(N) { }

};

struct FSharedHelper
{
	template <typename T, typename U> requires (CSameAs<T, TDecay<T>> && CSameAs<U, TDecay<U>>
		&& (CTSharedRef<T> || CTSharedPtr<T>) && (CTSharedRef<U> || CTSharedPtr<U>))
	static T& CopySharedReference(T& This, const U& InValue)
	{
		if (This.Controller == InValue.Controller)
		{
			This.Pointer = InValue.Pointer;
			return This;
		}

		if constexpr (CTSharedRef<T>)
		{
			This.Controller->ReleaseSharedReference();
		}
		else if (This.Controller != nullptr)
		{
			This.Controller->ReleaseSharedReference();
		}

		This.Pointer    = InValue.Pointer;
		This.Controller = InValue.Controller;

		if constexpr (CTSharedRef<T> || CTSharedRef<U>)
		{
			This.Controller->AddSharedReference();
		}
		else if (This.Controller != nullptr)
		{
			This.Controller->AddSharedReference();
		}

		return This;
	}

	template <typename T, typename U> requires (CSameAs<T, TDecay<T>> && CSameAs<U, TDecay<U>>
		&& (CTSharedRef<T> || CTSharedPtr<T>) && (CTSharedRef<U> || CTSharedPtr<U>))
	static T& MoveSharedReference(T& This, U&& InValue)
	{
		if constexpr (CTSharedRef<T>)
		{
			Swap(This.Pointer,    InValue.Pointer);
			Swap(This.Controller, InValue.Controller);
		}
		else if constexpr (CTSharedPtr<T> && CTSharedPtr<U>)
		{
			if (&InValue == &This) UNLIKELY return This;

			if (This.Controller != nullptr)
			{
				This.Controller->ReleaseSharedReference();
			}

			This.Pointer    = Exchange(InValue.Pointer,    nullptr);
			This.Controller = Exchange(InValue.Controller, nullptr);
		}
		else
		{
			CopySharedReference(This, InValue);
		}

		return This;
	}

	template <typename T, typename U> requires (CSameAs<T, TDecay<T>> && CSameAs<U, TDecay<U>>
		&& CTWeakPtr<T> && (CTSharedRef<U> || CTSharedPtr<U> || CTWeakPtr<U>))
	static T& CopyWeakReference(T& This, const U& InValue)
	{
		if constexpr (CTWeakPtr<T> && CTWeakPtr<U>)
		{
			if (This.Controller == InValue.Controller)
			{
				This.Pointer = InValue.Pointer;
				return This;
			}
		}

		if (This.Controller != nullptr)
		{
			This.Controller->ReleaseWeakReference();
		}

		This.Pointer    = InValue.Pointer;
		This.Controller = InValue.Controller;

		if constexpr (CTSharedRef<U>)
		{
			This.Controller->AddWeakReference();
		}
		else if (This.Controller != nullptr)
		{
			This.Controller->AddWeakReference();
		}

		return This;
	}

	template <typename T, typename U> requires (CSameAs<T, TDecay<T>> && CSameAs<U, TDecay<U>>
		&& CTWeakPtr<T> && (CTSharedRef<U> || CTSharedPtr<U> || CTWeakPtr<U>))
	static T& MoveWeakReference(T& This, U&& InValue)
	{
		if constexpr (CTWeakPtr<T> && CTWeakPtr<U>)
		{
			if (&InValue == &This) UNLIKELY return This;
			
			if (This.Controller != nullptr)
			{
				This.Controller->ReleaseWeakReference();
			}
			
			This.Pointer    = Exchange(InValue.Pointer,    nullptr);
			This.Controller = Exchange(InValue.Controller, nullptr);
		}
		else
		{
			CopyWeakReference(This, InValue);
		}

		return This;
	}

};

template <typename T>
class TSharedProxy : private FSingleton
{
public:

	FORCEINLINE TSharedProxy(TRemoveExtent<T>* InPtr, FSharedController* InController)
		: Pointer(InPtr), Controller(InController)
	{ }

	template <typename U> requires (CArray<T> == CArray<U> && ((!CArray<U> && CConvertibleTo<T(*)[], U(*)[]>)
		|| (CArray<U> && CConvertibleTo<TRemoveExtent<T>(*)[], TRemoveExtent<U>(*)[]>)))
	NODISCARD FORCEINLINE operator TSharedRef<U>() &&
	{
		check_code({ return TSharedRef<U>(Pointer, Exchange(Controller, nullptr)); });
		return TSharedRef<U>(Pointer, Controller);
	}

	template <typename U> requires (CArray<T> == CArray<U> && ((!CArray<U> && CConvertibleTo<T(*)[], U(*)[]>)
		|| (CArray<U> && CConvertibleTo<TRemoveExtent<T>(*)[], TRemoveExtent<U>(*)[]>)))
	NODISCARD FORCEINLINE operator TSharedPtr<U>() &&
	{
		check_code({ return TSharedPtr<U>(Pointer, Exchange(Controller, nullptr)); });
		return TSharedPtr<U>(Pointer, Controller);
	}

#	if DO_CHECK

	FORCEINLINE ~TSharedProxy() { checkf(Controller == nullptr, TEXT("The return value from MakeShared() is incorrectly ignored.")); }

#	endif

private:

	TRemoveExtent<T>*  Pointer;
	FSharedController* Controller;

};

struct FSharedPtrConstructor { explicit FSharedPtrConstructor() = default; };

inline constexpr FSharedPtrConstructor SharedPtrConstructor{ };

NAMESPACE_PRIVATE_END

/**
 * Derive your class from TSharedFromThis to enable access to a TSharedRef directly from an object instance
 * that's already been allocated. Note that when your class is managed indirectly rather than directly,
 * it is NOT enable access to a TSharedRef, for example managed by the array version of the shared pointer.
 */
template <typename T> requires (CClass<T>)
class TSharedFromThis
{
public:

	/**
	 * Provides access to a shared reference to this object.
	 * Note that is only valid to call this after a shared pointer to the object has already been created.
	 * Also note that it is illegal to call this in the object's destructor.
	 */
	FORCEINLINE TSharedRef<T> AsShared()
	{
		TSharedPtr<T> SharedThis(AsWeak().Lock());
		checkf(SharedThis.Get() == this, TEXT("Your class is now not directly managed. Please check DoesSharedInstanceExist()."));
		return MoveTemp(SharedThis).ToSharedRef();
	}

	/**
	 * Provides access to a shared reference to this object.
	 * Note that is only valid to call this after a shared pointer to the object has already been created.
	 * Also note that it is illegal to call this in the object's destructor.
	 */
	FORCEINLINE TSharedRef<const T> AsShared() const
	{
		TSharedPtr<const T> SharedThis(AsWeak().Lock());
		checkf(SharedThis.Get() == this, TEXT("Your class is now not directly managed. Please check DoesSharedInstanceExist()."));
		return MoveTemp(SharedThis).ToSharedRef();
	}

	/** Provides access to a weak reference to this object. */
	FORCEINLINE TWeakPtr<T> AsWeak()
	{
		return WeakThis;
	}

	/** Provides access to a weak reference to this object. */
	FORCEINLINE TWeakPtr<const T> AsWeak() const
	{
		return WeakThis;
	}

	/** Checks whether our referenced instance is valid, i.e. whether it's safe to call AsShared() or AsWeak(). */
	FORCEINLINE bool DoesSharedInstanceExist() const
	{
		return !WeakThis.Expired();
	}

protected:

	FORCEINLINE constexpr TSharedFromThis() : WeakThis() { }

	FORCEINLINE TSharedFromThis(const TSharedFromThis&) : TSharedFromThis() { }

	FORCEINLINE TSharedFromThis& operator=(const TSharedFromThis&) { return *this; }

	FORCEINLINE ~TSharedFromThis() = default;

private:

	using SharedFromThisType = TSharedFromThis;

	// Here it is updated by the private constructor of TSharedRef or TSharedPtr.
	mutable TWeakPtr<T> WeakThis;

	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TSharedRef;
	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TSharedPtr;
	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TWeakPtr;

};

/** Shared-ownership non-nullable smart pointer. Use this when you need an object's lifetime to be managed by a shared smart pointer. */
template <typename T> requires (CObject<T> && !CBoundedArray<T>)
class TSharedRef final
{
private:

	using Helper = NAMESPACE_PRIVATE::FSharedHelper;

public:

	using ElementType = T;
	using WeakType    = TWeakPtr<T>;

	/** TSharedRef cannot be initialized by nullptr. */
	TSharedRef() = delete;

	/** TSharedRef cannot be initialized by nullptr. */
	TSharedRef(nullptr_t) = delete;

	/** Constructs a shared reference that owns the specified object. Must not be nullptr. */
	FORCEINLINE explicit TSharedRef(T* InPtr) : TSharedRef(InPtr, TDefaultDelete<T>()) { }

	/** Constructs a shared reference that owns the specified object with a deleter. Must not be nullptr. */
	template <typename E> requires (CConstructibleFrom<TDecay<E>, E>
		&& CInvocable<TDecay<E>, TRemoveExtent<T>*> && CDestructible<TDecay<E>>)
	FORCEINLINE TSharedRef(T* InPtr, E&& InDeleter)
		: TSharedRef(InPtr, new NAMESPACE_PRIVATE::TSharedControllerWithDeleter<T, TDecay<E>>(InPtr, Forward<E>(InDeleter)))
	{
		checkf(InPtr != nullptr, TEXT("TSharedRef cannot be initialized by nullptr. Please use TSharedPtr."));
	}

	/**
	 * Aliasing constructor used to create a shared reference which shares its reference count with
	 * another shared object, but pointing to a different object, typically a subobject.
	 * Must not be nullptr.
	 *
	 * @param  InValue - The shared reference whose reference count should be shared.
	 * @param  InPtr   - The object pointer to use (instead of the incoming shared pointer's object).
	 */
	template <typename U>
	FORCEINLINE TSharedRef(const TSharedRef<U>& InValue, T* InPtr)
		: Pointer(InPtr), Controller(InValue.Controller)
	{
		checkf(InPtr != nullptr, TEXT("TSharedRef cannot be initialized by nullptr. Please use TSharedPtr."));

		Controller->AddSharedReference();
	}
	
	/**
	 * Aliasing constructor used to create a shared reference which shares its reference count with
	 * another shared object, but pointing to a different object, typically a subobject.
	 * Must not be nullptr.
	 *
	 * @param  InValue - The shared reference whose reference count should be shared.
	 * @param  InPtr   - The object pointer to use (instead of the incoming shared pointer's object).
	 */
	template <typename U>
	FORCEINLINE TSharedRef(TSharedRef<U>&& InValue, T* InPtr): TSharedRef(InValue, InPtr) { }

	/** Constructs a TSharedRef which shares ownership of the object managed by 'InValue'. */
	FORCEINLINE TSharedRef(const TSharedRef& InValue) : TSharedRef(InValue, InValue.Get()) { }

	/** Constructs a TSharedRef which shares ownership of the object managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE TSharedRef(const TSharedRef<U>& InValue) : TSharedRef(InValue, InValue.Get()) { }

	/** Constructs a TSharedRef which shares ownership of the object managed by 'InValue'. */
	FORCEINLINE TSharedRef(TSharedRef&& InValue) : TSharedRef(InValue) { }

	/** Constructs a TSharedRef which shares ownership of the object managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE TSharedRef(TSharedRef<U>&& InValue) : TSharedRef(InValue) { }

	/** If this owns an object and it is the last TSharedRef owning it, the object is destroyed through the owned deleter. */
	FORCEINLINE ~TSharedRef() { Controller->ReleaseSharedReference(); }

	/** Replaces the managed object with the one managed by 'InValue'. */
	FORCEINLINE TSharedRef& operator=(const TSharedRef& InValue) { return Helper::CopySharedReference(*this, InValue); }

	/** Replaces the managed object with the one managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE TSharedRef& operator=(const TSharedRef<U>& InValue) { return Helper::CopySharedReference(*this, InValue); }

	/** Replaces the managed object with the one managed by 'InValue'. */
	FORCEINLINE TSharedRef& operator=(TSharedRef&& InValue) { return Helper::MoveSharedReference(*this, MoveTemp(InValue)); }

	/** Replaces the managed object with the one managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE TSharedRef& operator=(TSharedRef<U>&& InValue) { return Helper::MoveSharedReference(*this, MoveTemp(InValue)); }

	/** Compares the pointer values of two TSharedRef. */
	NODISCARD friend FORCEINLINE constexpr bool operator==(const TSharedRef& LHS, const TSharedRef& RHS) { return LHS.Get() == RHS.Get(); }

	/** Compares the pointer values of two TSharedRef. */
	NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const TSharedRef& LHS, const TSharedRef& RHS) { return LHS.Get() <=> RHS.Get(); }

	/** Compares the pointer values with a raw pointer. */
	NODISCARD FORCEINLINE constexpr bool operator==(T* InPtr) const& { return Get() == InPtr; }

	/** Compares the pointer values with a raw pointer. */
	NODISCARD FORCEINLINE constexpr strong_ordering operator<=>(T* InPtr) const& { return Get() <=> InPtr; }

	/** TSharedRef cannot be initialized by nullptr. */
	void Reset(nullptr_t) = delete;

	/** Replaces the managed object. */
	FORCEINLINE void Reset(T* InPtr) { *this = MoveTemp(TSharedRef(InPtr)); }

	/** TSharedRef cannot be initialized by nullptr. */
	template <typename E>
	void Reset(nullptr_t, E&&) = delete;

	/** Replaces the managed object with a deleter. */
	template <typename E> requires (CConstructibleFrom<TDecay<E>, E>
		&& CInvocable<TDecay<E>, TRemoveExtent<T>*> && CDestructible<TDecay<E>>)
	FORCEINLINE void Reset(T* InPtr, E&& InDeleter) { *this = MoveTemp(TSharedRef(InPtr, Forward<E>(InDeleter))); }

	/** @return The pointer to the managed object. */
	NODISCARD FORCEINLINE constexpr T* Get() const { return Pointer; }

	/** @return The pointer to the owned deleter or nullptr. */
	template <typename E> requires (CInvocable<TDecay<E>, TRemoveExtent<T>*> && (CDestructible<E> || CLValueReference<E>))
	NODISCARD FORCEINLINE E* GetDeleter() const
	{
		const auto ControllerWithDeleter = dynamic_cast<NAMESPACE_PRIVATE::TSharedControllerWithDeleter<T, E>*>(Controller);
		return ControllerWithDeleter != nullptr ? &ControllerWithDeleter->GetDeleter() : nullptr;
	}

	/** @return The a reference or pointer to the object owned by *this, i.e. Get(). */
	NODISCARD FORCEINLINE constexpr T& operator*()  const { return *Get(); }
	NODISCARD FORCEINLINE constexpr T* operator->() const { return  Get(); }

	/**
	 * Returns the number of shared references to this object (including this reference.)
	 * IMPORTANT: With multi-threading this is only an estimate.
	 *
	 * @return The number of instances managing the current object.
	 */
	NODISCARD FORCEINLINE size_t GetSharedReferenceCount() const { return Controller->GetSharedReferenceCount(); }

	/**
	 * Checks if this is the only instance managing the current object, i.e. whether GetSharedReferenceCount() == 1.
	 * IMPORTANT: With multi-threading this is only an estimate.
	 *
	 * @return true if *this is the only shared_ptr instance managing the current object, false otherwise.
	 */
	NODISCARD FORCEINLINE bool IsUnique() const { return GetSharedReferenceCount() == 1; }

	/**
	 * Checks whether this TSharedRef precedes other in implementation defined owner-based (as opposed to value-based) order.
	 * This ensures that the ordering of TSharedRef constructed by the aliasing constructor is not affected by object pointer.
	 *
	 * @return The ordering of the addresses of the control blocks.
	 */
	template <typename U = T> NODISCARD FORCEINLINE strong_ordering OwnerCompare(const TSharedRef<U>& InValue) const { return Controller <=> InValue.Controller; }
	template <typename U = T> NODISCARD FORCEINLINE strong_ordering OwnerCompare(const TSharedPtr<U>& InValue) const { return Controller <=> InValue.Controller; }

	/** Overloads the GetTypeHash algorithm for TSharedRef. */
	NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TSharedRef& A) { return GetTypeHash(A.Get()); }

	/** Overloads the Swap algorithm for TSharedRef. */
	friend FORCEINLINE constexpr void Swap(TSharedRef& A, TSharedRef& B)
	{
		Swap(A.Pointer, B.Pointer);
		Swap(A.Controller, B.Controller);
	}

private:

	T* Pointer;
	NAMESPACE_PRIVATE::FSharedController* Controller;

	FORCEINLINE TSharedRef(NAMESPACE_PRIVATE::FSharedPtrConstructor, const TSharedPtr<T>& InValue)
		: Pointer(InValue.Pointer), Controller(InValue.Controller)
	{
		Controller->AddSharedReference();
	}

	FORCEINLINE TSharedRef(NAMESPACE_PRIVATE::FSharedPtrConstructor, TSharedPtr<T>&& InValue)
		: Pointer(Exchange(InValue.Pointer, nullptr))
		, Controller(Exchange(InValue.Controller, nullptr))
	{ }

	FORCEINLINE TSharedRef(T* InPtr, NAMESPACE_PRIVATE::FSharedController* InController)
		: Pointer(InPtr), Controller(InController)
	{
		check(!((Pointer == nullptr) ^ (Controller == nullptr)));

		if constexpr (CClass<T> && !CVolatile<T> && requires { typename T::SharedFromThisType; })
		{
			using SharedFromThisType = T::SharedFromThisType;

			if constexpr (CDerivedFrom<T, SharedFromThisType>)
			{
				if (Pointer != nullptr)
				{
					const SharedFromThisType& SharedFromThis = *Pointer;
					checkf(!SharedFromThis.DoesSharedInstanceExist(), TEXT("This object is incorrectly managed by multiple TSharedRef or TSharedPtr."));
					SharedFromThis.WeakThis = ConstCast<TRemoveCV<T>>(*this);
				}
			}
		}
	}

	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TSharedRef;
	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TSharedPtr;
	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TWeakPtr;

	template <typename U> friend class NAMESPACE_PRIVATE::TSharedProxy;

	friend struct NAMESPACE_PRIVATE::FSharedHelper;

};

/** Shared-ownership non-nullable smart pointer. Use this when you need an array's lifetime to be managed by a shared smart pointer. */
template <typename T>
class TSharedRef<T[]> final
{
private:

	using Helper = NAMESPACE_PRIVATE::FSharedHelper;

public:

	using ElementType = T;
	using WeakType    = TWeakPtr<T>;

	/** TSharedRef cannot be initialized by nullptr. */
	TSharedRef() = delete;

	/** TSharedRef cannot be initialized by nullptr. */
	TSharedRef(nullptr_t) = delete;

	/** Constructs a shared reference that owns the specified array. Must not be nullptr. */
	template <typename U = T*> requires (CPointer<U>&& CConvertibleTo<TRemovePointer<U>(*)[], T(*)[]>)
	FORCEINLINE explicit TSharedRef(U InPtr) : TSharedRef(InPtr, TDefaultDelete<T[]>()) { }

	/** Constructs a shared reference that owns the specified array with a deleter. Must not be nullptr. */
	template <typename U = T*, typename E> requires (CConstructibleFrom<TDecay<E>, E>
		&& CInvocable<TDecay<E>, TRemoveExtent<T>*> && CDestructible<TDecay<E>>
		&& (CNullPointer<U> || (CPointer<U> && CConvertibleTo<TRemovePointer<U>(*)[], T(*)[]>)))
	FORCEINLINE TSharedRef(U InPtr, E&& InDeleter)
		: TSharedRef(InPtr, new NAMESPACE_PRIVATE::TSharedControllerWithDeleter<T, TDecay<E>>(InPtr, Forward<E>(InDeleter)))
	{
		checkf(InPtr != nullptr, TEXT("TSharedRef cannot be initialized by nullptr. Please use TSharedPtr."));
	}

	/**
	 * Aliasing constructor used to create a shared reference which shares its reference count with
	 * another shared array, but pointing to a different array, typically a subobject.
	 * Must not be nullptr.
	 *
	 * @param  InValue - The shared reference whose reference count should be shared.
	 * @param  InPtr   - The array pointer to use (instead of the incoming shared pointer's array).
	 */
	template <typename U, typename V = T*> requires (CNullPointer<V> || (CPointer<V> && CConvertibleTo<TRemovePointer<V>(*)[], T(*)[]>))
	FORCEINLINE TSharedRef(const TSharedRef<U>& InValue, V InPtr)
		: Pointer(InPtr), Controller(InValue.Controller)
	{
		checkf(InPtr != nullptr, TEXT("TSharedRef cannot be initialized by nullptr. Please use TSharedPtr."));

		Controller->AddSharedReference();
	}

	/**
	 * Aliasing constructor used to create a shared reference which shares its reference count with
	 * another shared array, but pointing to a different array, typically a subobject.
	 * Must not be nullptr.
	 *
	 * @param  InValue - The shared reference whose reference count should be shared.
	 * @param  InPtr   - The array pointer to use (instead of the incoming shared pointer's array).
	 */
	template <typename U, typename V = T*> requires (CNullPointer<V> || (CPointer<V> && CConvertibleTo<TRemovePointer<V>(*)[], T(*)[]>))
	FORCEINLINE TSharedRef(TSharedRef<U>&& InValue, V InPtr) : TSharedRef(InValue, InPtr) { }

	/** Constructs a TSharedRef which shares ownership of the array managed by 'InValue'. */
	FORCEINLINE TSharedRef(const TSharedRef& InValue) : TSharedRef(InValue, InValue.Get()) { }

	/** Constructs a TSharedRef which shares ownership of the array managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE TSharedRef(const TSharedRef<U>& InValue) : TSharedRef(InValue, InValue.Get()) { }

	/** Constructs a TSharedRef which shares ownership of the array managed by 'InValue'. */
	FORCEINLINE TSharedRef(TSharedRef&& InValue) : TSharedRef(InValue) { }

	/** Constructs a TSharedRef which shares ownership of the array managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE TSharedRef(TSharedRef<U>&& InValue) : TSharedRef(InValue) { }

	/** If this owns an array and it is the last TSharedRef owning it, the array is destroyed through the owned deleter. */
	FORCEINLINE ~TSharedRef() { Controller->ReleaseSharedReference(); }

	/** Replaces the managed array with the one managed by 'InValue'. */
	FORCEINLINE TSharedRef& operator=(const TSharedRef& InValue) { return Helper::CopySharedReference(*this, InValue); }

	/** Replaces the managed array with the one managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE TSharedRef& operator=(const TSharedRef<U>& InValue) { return Helper::CopySharedReference(*this, InValue); }

	/** Replaces the managed array with the one managed by 'InValue'. */
	FORCEINLINE TSharedRef& operator=(TSharedRef&& InValue) { return Helper::MoveSharedReference(*this, MoveTemp(InValue)); }

	/** Replaces the managed array with the one managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE TSharedRef& operator=(TSharedRef<U>&& InValue) { return Helper::MoveSharedReference(*this, MoveTemp(InValue)); }

	/** Compares the pointer values of two TSharedRef. */
	NODISCARD friend FORCEINLINE constexpr bool operator==(const TSharedRef& LHS, const TSharedRef& RHS) { return LHS.Get() == RHS.Get(); }

	/** Compares the pointer values of two TSharedRef. */
	NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const TSharedRef& LHS, const TSharedRef& RHS) { return LHS.Get() <=> RHS.Get(); }

	/** Compares the pointer values with a raw pointer. */
	template <typename U = T*> requires (CNullPointer<U> || (CPointer<U> && CConvertibleTo<TRemovePointer<U>(*)[], T(*)[]>))
	NODISCARD FORCEINLINE constexpr bool operator==(U InPtr) const& { return Get() == InPtr; }

	/** Compares the pointer values with a raw pointer. */
	template <typename U = T*> requires (CNullPointer<U> || (CPointer<U> && CConvertibleTo<TRemovePointer<U>(*)[], T(*)[]>))
	NODISCARD FORCEINLINE constexpr strong_ordering operator<=>(U InPtr) const& { return Get() <=> InPtr; }

	/** TSharedRef cannot be initialized by nullptr. */
	void Reset(nullptr_t) = delete;

	/** Replaces the managed array. */
	template <typename U> requires (CPointer<U> && CConvertibleTo<TRemovePointer<U>(*)[], T(*)[]>)
	FORCEINLINE void Reset(U InPtr) { *this = MoveTemp(TSharedRef(InPtr)); }

	/** TSharedRef cannot be initialized by nullptr. */
	template <typename E>
	void Reset(nullptr_t, E&&) = delete;

	/** Replaces the managed array with a deleter. */
	template <typename U, typename E> requires (CPointer<U> && CConvertibleTo<TRemovePointer<U>(*)[], T(*)[]>
		&& CConstructibleFrom<TDecay<E>, E> && CInvocable<TDecay<E>, TRemoveExtent<T>*> && CDestructible<TDecay<E>>)
	FORCEINLINE void Reset(U InPtr, E&& InDeleter) { *this = MoveTemp(TSharedRef(InPtr, Forward<E>(InDeleter))); }

	/** @return The pointer to the managed array. */
	NODISCARD FORCEINLINE constexpr T* Get() const { return Pointer; }

	/** @return The pointer to the owned deleter or nullptr. */
	template <typename E> requires (CInvocable<TDecay<E>, TRemoveExtent<T>*> && (CDestructible<E> || CLValueReference<E>))
	NODISCARD FORCEINLINE E* GetDeleter() const
	{
		const auto ControllerWithDeleter = dynamic_cast<NAMESPACE_PRIVATE::TSharedControllerWithDeleter<T, E>*>(Controller);
		return ControllerWithDeleter != nullptr ? &ControllerWithDeleter->GetDeleter() : nullptr;
	}

	/** @return The element at index, i.e. Get()[Index]. */
	NODISCARD FORCEINLINE constexpr T& operator[](size_t Index) const { return Get()[Index]; }

	/**
	 * Returns the number of shared references to this array (including this reference.)
	 * IMPORTANT: With multi-threading this is only an estimate.
	 *
	 * @return The number of instances managing the current array.
	 */
	NODISCARD FORCEINLINE size_t GetSharedReferenceCount() const { return Controller->GetSharedReferenceCount(); }

	/**
	 * Checks if this is the only instance managing the current array, i.e. whether GetSharedReferenceCount() == 1.
	 * IMPORTANT: With multi-threading this is only an estimate.
	 *
	 * @return true if *this is the only shared_ptr instance managing the current array, false otherwise.
	 */
	NODISCARD FORCEINLINE bool IsUnique() const { return GetSharedReferenceCount() == 1; }

	/**
	 * Checks whether this TSharedRef precedes other in implementation defined owner-based (as opposed to value-based) order.
	 * This ensures that the ordering of TSharedRef constructed by the aliasing constructor is not affected by array pointer.
	 *
	 * @return The ordering of the addresses of the control blocks.
	 */
	template <typename U = T> NODISCARD FORCEINLINE strong_ordering OwnerCompare(const TSharedRef<U>& InValue) const { return Controller <=> InValue.Controller; }
	template <typename U = T> NODISCARD FORCEINLINE strong_ordering OwnerCompare(const TSharedPtr<U>& InValue) const { return Controller <=> InValue.Controller; }

	/** Overloads the GetTypeHash algorithm for TSharedRef. */
	NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TSharedRef& A) { return GetTypeHash(A.Get()); }

	/** Overloads the Swap algorithm for TSharedRef. */
	friend FORCEINLINE constexpr void Swap(TSharedRef& A, TSharedRef& B)
	{
		Swap(A.Pointer, B.Pointer);
		Swap(A.Controller, B.Controller);
	}

private:

	T* Pointer;
	NAMESPACE_PRIVATE::FSharedController* Controller;

	FORCEINLINE TSharedRef(NAMESPACE_PRIVATE::FSharedPtrConstructor, const TSharedPtr<T[]>& InValue)
		: Pointer(InValue.Pointer), Controller(InValue.Controller)
	{
		Controller->AddSharedReference();
	}

	FORCEINLINE TSharedRef(NAMESPACE_PRIVATE::FSharedPtrConstructor, TSharedPtr<T[]>&& InValue)
		: Pointer(Exchange(InValue.Pointer, nullptr))
		, Controller(Exchange(InValue.Controller, nullptr))
	{ }

	FORCEINLINE TSharedRef(T* InPtr, NAMESPACE_PRIVATE::FSharedController* InController)
		: Pointer(InPtr), Controller(InController)
	{
		check(!((Pointer == nullptr) ^ (Controller == nullptr)));
	}

	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TSharedRef;
	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TSharedPtr;
	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TWeakPtr;

	template <typename U> friend class NAMESPACE_PRIVATE::TSharedProxy;

	friend struct NAMESPACE_PRIVATE::FSharedHelper;

};

/** Shared-ownership smart pointer. Use this when you need an object's lifetime to be managed by a shared smart pointer. */
template <typename T> requires (CObject<T> && !CBoundedArray<T>)
class TSharedPtr final
{
private:

	using Helper = NAMESPACE_PRIVATE::FSharedHelper;

public:

	using ElementType = T;
	using WeakType    = TWeakPtr<T>;
	
	/** Constructs an empty shared pointer. */
	FORCEINLINE constexpr TSharedPtr() : Pointer(nullptr), Controller(nullptr) { }

	/** Constructs an empty shared pointer. */
	FORCEINLINE constexpr TSharedPtr(nullptr_t) : TSharedPtr() { }

	/** Constructs a shared pointer that owns the specified object. Note that nullptr is not managed like std. */
	FORCEINLINE explicit TSharedPtr(T* InPtr) : TSharedPtr(InPtr, TDefaultDelete<T>()) { }

	/** Constructs a shared pointer that owns the specified object with a deleter. Note that nullptr is not managed like std. */
	template <typename E> requires (CConstructibleFrom<TDecay<E>, E>
		&& CInvocable<TDecay<E>, TRemoveExtent<T>*> && CDestructible<TDecay<E>>)
	FORCEINLINE TSharedPtr(T* InPtr, E&& InDeleter)
		: TSharedPtr(InPtr, InPtr != nullptr ? new NAMESPACE_PRIVATE::TSharedControllerWithDeleter<T, TDecay<E>>(InPtr, Forward<E>(InDeleter)) : nullptr)
	{ }

	/**
	 * Aliasing constructor used to create a shared reference which shares its reference count with
	 * another shared object, but pointing to a different object, typically a subobject.
	 * Must not be nullptr.
	 *
	 * @param  InValue - The shared reference whose reference count should be shared.
	 * @param  InPtr   - The object pointer to use (instead of the incoming shared pointer's object).
	 */
	template <typename U>
	FORCEINLINE TSharedPtr(const TSharedPtr<U>& InValue, T* InPtr)
		: Pointer(InPtr), Controller(InValue.Controller)
	{
		checkf(!((Pointer == nullptr) ^ (Controller == nullptr)), TEXT("TSharedPtr's aliasing constructor cannot be initialized by nullptr."));

		if (Controller != nullptr)
		{
			Controller->AddSharedReference();
		}
	}

	/**
	 * Aliasing constructor used to create a shared reference which shares its reference count with
	 * another shared object, but pointing to a different object, typically a subobject.
	 * Must not be nullptr.
	 *
	 * @param  InValue - The shared reference whose reference count should be shared.
	 * @param  InPtr   - The object pointer to use (instead of the incoming shared pointer's object).
	 */
	template <typename U>
	FORCEINLINE TSharedPtr(const TSharedRef<U>& InValue, T* InPtr)
	{
		new (this) TSharedRef<T>(InValue, InPtr);
	}

	/**
	 * Aliasing constructor used to create a shared reference which shares its reference count with
	 * another shared object, but pointing to a different object, typically a subobject.
	 * Must not be nullptr.
	 *
	 * @param  InValue - The shared reference whose reference count should be shared.
	 * @param  InPtr   - The object pointer to use (instead of the incoming shared pointer's object).
	 */
	template <typename U>
	FORCEINLINE TSharedPtr(TSharedPtr<U>&& InValue, T* InPtr)
		: Pointer(InPtr), Controller(InValue.Controller)
	{
		checkf(!((Pointer == nullptr) ^ (Controller == nullptr)), TEXT("TSharedPtr's aliasing constructor cannot be initialized by nullptr."));

		InValue.Pointer    = nullptr;
		InValue.Controller = nullptr;
	}

	/** Constructs a TSharedPtr which shares ownership of the object managed by 'InValue'. */
	FORCEINLINE TSharedPtr(const TSharedPtr& InValue) : TSharedPtr(InValue, InValue.Get()) { }

	/** Constructs a TSharedPtr which shares ownership of the object managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE TSharedPtr(const TSharedPtr<U>& InValue) : TSharedPtr(InValue, InValue.Get()) { }

	/** Constructs a TSharedPtr which shares ownership of the object managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE TSharedPtr(const TSharedRef<U>& InValue) : TSharedPtr(InValue, InValue.Get()) { }

	/** Constructs a TSharedPtr which shares ownership of the object managed by 'InValue'. */
	FORCEINLINE TSharedPtr(TSharedPtr&& InValue) : TSharedPtr(MoveTemp(InValue), InValue.Get()) { }

	/** Constructs a TSharedPtr which shares ownership of the object managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE TSharedPtr(TSharedPtr<U>&& InValue) : TSharedPtr(MoveTemp(InValue), InValue.Get()) { }

	/** Constructs a TSharedPtr which gets ownership of the object managed by 'InValue'. */
	template <typename U, typename E> requires (CConvertibleTo<U*, T*> && !CArray<U> && (CDestructible<E> || CLValueReference<E>))
	FORCEINLINE TSharedPtr(TUniquePtr<U, E>&& InValue) : TSharedPtr(InValue.Release(), Forward<E>(InValue.GetDeleter())) { }

	/** If this owns an object and it is the last TSharedPtr owning it, the object is destroyed through the owned deleter. */
	FORCEINLINE ~TSharedPtr() { if (Controller != nullptr) Controller->ReleaseSharedReference(); }

	/** Replaces the managed object with the one managed by 'InValue'. */
	FORCEINLINE TSharedPtr& operator=(const TSharedPtr& InValue) { return Helper::CopySharedReference(*this, InValue); }

	/** Replaces the managed object with the one managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE TSharedPtr& operator=(const TSharedPtr<U>& InValue) { return Helper::CopySharedReference(*this, InValue); }

	/** Replaces the managed object with the one managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE TSharedPtr& operator=(const TSharedRef<U>& InValue) { return Helper::CopySharedReference(*this, InValue); }

	/** Replaces the managed object with the one managed by 'InValue'. */
	FORCEINLINE TSharedPtr& operator=(TSharedPtr&& InValue) { return Helper::MoveSharedReference(*this, MoveTemp(InValue)); }

	/** Replaces the managed object with the one managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE TSharedPtr& operator=(TSharedPtr<U>&& InValue) { return Helper::MoveSharedReference(*this, MoveTemp(InValue)); }

	/** Replaces the managed object with the one managed by 'InValue'. */
	template <typename U, typename E> requires (CConvertibleTo<U*, T*> && !CArray<U> && (CDestructible<E> || CLValueReference<E>))
	FORCEINLINE TSharedPtr& operator=(TUniquePtr<U, E>&& InValue) { return Swap(*this, TSharedPtr(MoveTemp(InValue))); }

	/** Effectively the same as calling Reset(). */
	FORCEINLINE TSharedPtr& operator=(nullptr_t) { Reset(); return *this; }

	/** Compares the pointer values of two TSharedPtr. */
	NODISCARD friend FORCEINLINE constexpr bool operator==(const TSharedPtr& LHS, const TSharedPtr& RHS) { return LHS.Get() == RHS.Get(); }

	/** Compares the pointer values of two TSharedPtr. */
	NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const TSharedPtr& LHS, const TSharedPtr& RHS) { return LHS.Get() <=> RHS.Get(); }

	/** Compares the pointer values with a raw pointer. */
	NODISCARD FORCEINLINE constexpr bool operator==(T* InPtr) const& { return Get() == InPtr; }

	/** Compares the pointer values with a raw pointer. */
	NODISCARD FORCEINLINE constexpr strong_ordering operator<=>(T* InPtr) const& { return Get() <=> InPtr; }

	/** Converts a shared pointer to a shared reference. The pointer MUST be valid or an assertion will trigger. */
	FORCEINLINE TSharedRef<T> ToSharedRef() const&
	{
		checkf(IsValid(), TEXT("TSharedRef cannot be initialized by nullptr."));
		return TSharedRef<T>(NAMESPACE_PRIVATE::SharedPtrConstructor, *this);
	}

	/** Converts a shared pointer to a shared reference. The pointer MUST be valid or an assertion will trigger. */
	FORCEINLINE TSharedRef<T> ToSharedRef() &&
	{
		checkf(IsValid(), TEXT("TSharedRef cannot be initialized by nullptr."));
		return TSharedRef<T>(NAMESPACE_PRIVATE::SharedPtrConstructor, *this);
	}

	/** Replaces the managed object. */
	FORCEINLINE void Reset(T* InPtr = nullptr) { *this = MoveTemp(TSharedPtr(InPtr)); }

	/** Replaces the managed object with a deleter. */
	template <typename E> requires (CConstructibleFrom<TDecay<E>, E>
		&& CInvocable<TDecay<E>, TRemoveExtent<T>*> && CDestructible<TDecay<E>>)
	FORCEINLINE void Reset(T* InPtr, E&& InDeleter) { *this = MoveTemp(TSharedPtr(InPtr, Forward<E>(InDeleter))); }

	/** @return The pointer to the managed object. */
	NODISCARD FORCEINLINE constexpr T* Get() const { return Pointer; }

	/** @return The pointer to the owned deleter or nullptr. */
	template <typename E> requires (CInvocable<TDecay<E>, TRemoveExtent<T>*> && (CDestructible<E> || CLValueReference<E>))
	NODISCARD FORCEINLINE E* GetDeleter() const
	{
		const auto ControllerWithDeleter = dynamic_cast<NAMESPACE_PRIVATE::TSharedControllerWithDeleter<T, E>*>(Controller);
		return ControllerWithDeleter != nullptr ? &ControllerWithDeleter->GetDeleter() : nullptr;
	}

	/** @return true if *this owns an object, false otherwise. */
	NODISCARD FORCEINLINE constexpr bool           IsValid() const { return Get() != nullptr; }
	NODISCARD FORCEINLINE constexpr explicit operator bool() const { return Get() != nullptr; }

	/** @return The a reference or pointer to the object owned by *this, i.e. Get(). */
	NODISCARD FORCEINLINE constexpr T& operator*()  const { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return *Get(); }
	NODISCARD FORCEINLINE constexpr T* operator->() const { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return  Get(); }

	/**
	 * Returns the number of shared references to this object (including this reference.)
	 * IMPORTANT: With multi-threading this is only an estimate.
	 *
	 * @return The number of instances managing the current object.
	 */
	NODISCARD FORCEINLINE size_t GetSharedReferenceCount() const { return Controller != nullptr ? Controller->GetSharedReferenceCount() : 0; }

	/**
	 * Checks if this is the only instance managing the current object, i.e. whether GetSharedReferenceCount() == 1.
	 * IMPORTANT: With multi-threading this is only an estimate.
	 *
	 * @return true if *this is the only shared_ptr instance managing the current object, false otherwise.
	 */
	NODISCARD FORCEINLINE bool IsUnique() const { return GetSharedReferenceCount() == 1; }

	/**
	 * Checks whether this TSharedPtr precedes other in implementation defined owner-based (as opposed to value-based) order.
	 * This ensures that the ordering of TSharedPtr constructed by the aliasing constructor is not affected by object pointer.
	 *
	 * @return The ordering of the addresses of the control blocks.
	 */
	template <typename U = T> NODISCARD FORCEINLINE strong_ordering OwnerCompare(const TSharedRef<U>& InValue) const { return Controller <=> InValue.Controller; }
	template <typename U = T> NODISCARD FORCEINLINE strong_ordering OwnerCompare(const TSharedPtr<U>& InValue) const { return Controller <=> InValue.Controller; }

	/** Overloads the GetTypeHash algorithm for TSharedPtr. */
	NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TSharedPtr& A) { return GetTypeHash(A.Get()); }

	/** Overloads the Swap algorithm for TSharedPtr. */
	friend FORCEINLINE constexpr void Swap(TSharedPtr& A, TSharedPtr& B)
	{
		Swap(A.Pointer, B.Pointer);
		Swap(A.Controller, B.Controller);
	}

private:

	T* Pointer;
	NAMESPACE_PRIVATE::FSharedController* Controller;
	
	FORCEINLINE TSharedPtr(const TWeakPtr<T>& InValue)
	{
		const bool bIsUnexpired = InValue.Controller != nullptr && InValue.Controller->AddSharedReferenceIfUnexpired();

		Pointer    = bIsUnexpired ? InValue.Pointer    : nullptr;
		Controller = bIsUnexpired ? InValue.Controller : nullptr;
	
	}

	FORCEINLINE TSharedPtr(T* InPtr, NAMESPACE_PRIVATE::FSharedController* InController)
		: Pointer(InPtr), Controller(InController)
	{
		check(!((Pointer == nullptr) ^ (Controller == nullptr)));

		if constexpr (CClass<T> && !CVolatile<T> && requires { typename T::SharedFromThisType; })
		{
			using SharedFromThisType = T::SharedFromThisType;

			if constexpr (CDerivedFrom<T, SharedFromThisType>)
			{
				if (Pointer != nullptr)
				{
					const SharedFromThisType& SharedFromThis = *Pointer;
					checkf(!SharedFromThis.DoesSharedInstanceExist(), TEXT("This object is incorrectly managed by multiple TSharedRef or TSharedPtr."));
					SharedFromThis.WeakThis = ConstCast<TRemoveCV<T>>(*this);
				}
			}
		}
	}

	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TSharedRef;
	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TSharedPtr;
	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TWeakPtr;

	template <typename U> friend class NAMESPACE_PRIVATE::TSharedProxy;

	friend struct NAMESPACE_PRIVATE::FSharedHelper;

};

/** Shared-ownership smart pointer. Use this when you need an array's lifetime to be managed by a shared smart pointer. */
template <typename T>
class TSharedPtr<T[]> final
{
private:

	using Helper = NAMESPACE_PRIVATE::FSharedHelper;

public:

	using ElementType = T;
	using WeakType    = TWeakPtr<T>;

	/** Constructs an empty shared pointer. */
	FORCEINLINE constexpr TSharedPtr() : Pointer(nullptr), Controller(nullptr) { }

	/** Constructs an empty shared pointer. */
	FORCEINLINE constexpr TSharedPtr(nullptr_t) : TSharedPtr() { }

	/** Constructs a shared pointer that owns the specified array. Note that nullptr is not managed like std. */
	template <typename U = T*> requires (CPointer<U> && CConvertibleTo<TRemovePointer<U>(*)[], T(*)[]>)
	FORCEINLINE explicit TSharedPtr(U InPtr) : TSharedPtr(InPtr, TDefaultDelete<T[]>()) { }

	/** Constructs a shared pointer that owns the specified array with a deleter. Note that nullptr is not managed like std. */
	template <typename U = T*, typename E> requires (CConstructibleFrom<TDecay<E>, E>
		&& CInvocable<TDecay<E>, TRemoveExtent<T>*> && CDestructible<TDecay<E>>
		&& (CNullPointer<U> || (CPointer<U> && CConvertibleTo<TRemovePointer<U>(*)[], T(*)[]>)))
	FORCEINLINE TSharedPtr(U InPtr, E&& InDeleter)
		: TSharedPtr(InPtr, InPtr != nullptr ? new NAMESPACE_PRIVATE::TSharedControllerWithDeleter<T, TDecay<E>>(InPtr, Forward<E>(InDeleter)) : nullptr)
	{ }

	/**
	 * Aliasing constructor used to create a shared reference which shares its reference count with
	 * another shared array, but pointing to a different array, typically a subobject.
	 * Must not be nullptr.
	 *
	 * @param  InValue - The shared reference whose reference count should be shared.
	 * @param  InPtr   - The array pointer to use (instead of the incoming shared pointer's array).
	 */
	template <typename U, typename V = T*> requires (CNullPointer<V> || (CPointer<V> && CConvertibleTo<TRemovePointer<V>(*)[], T(*)[]>))
	FORCEINLINE TSharedPtr(const TSharedPtr<U>& InValue, V InPtr)
		: Pointer(InPtr), Controller(InValue.Controller)
	{
		checkf(!((Pointer == nullptr) ^ (Controller == nullptr)), TEXT("TSharedPtr's aliasing constructor cannot be initialized by nullptr."));

		if (Controller != nullptr)
		{
			Controller->AddSharedReference();
		}
	}

	/**
	 * Aliasing constructor used to create a shared reference which shares its reference count with
	 * another shared array, but pointing to a different array, typically a subobject.
	 * Must not be nullptr.
	 *
	 * @param  InValue - The shared reference whose reference count should be shared.
	 * @param  InPtr   - The array pointer to use (instead of the incoming shared pointer's array).
	 */
	template <typename U, typename V = T*> requires (CNullPointer<V> || (CPointer<V> && CConvertibleTo<TRemovePointer<V>(*)[], T(*)[]>))
	FORCEINLINE TSharedPtr(const TSharedRef<U>& InValue, V InPtr)
	{
		new (this) TSharedRef<T[]>(InValue, InPtr);
	}

	/**
	 * Aliasing constructor used to create a shared reference which shares its reference count with
	 * another shared array, but pointing to a different array, typically a subobject.
	 * Must not be nullptr.
	 *
	 * @param  InValue - The shared reference whose reference count should be shared.
	 * @param  InPtr   - The array pointer to use (instead of the incoming shared pointer's array).
	 */
	template <typename U, typename V = T*> requires (CNullPointer<V> || (CPointer<V> && CConvertibleTo<TRemovePointer<V>(*)[], T(*)[]>))
	FORCEINLINE TSharedPtr(TSharedPtr<U>&& InValue, V InPtr)
		: Pointer(InPtr), Controller(InValue.Controller)
	{
		checkf(!((Pointer == nullptr) ^ (Controller == nullptr)), TEXT("TSharedPtr's aliasing constructor cannot be initialized by nullptr."));

		InValue.Pointer    = nullptr;
		InValue.Controller = nullptr;
	}

	/** Constructs a TSharedPtr which shares ownership of the array managed by 'InValue'. */
	FORCEINLINE TSharedPtr(const TSharedPtr& InValue) : TSharedPtr(InValue, InValue.Get()) { }

	/** Constructs a TSharedPtr which shares ownership of the array managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE TSharedPtr(const TSharedPtr<U>& InValue) : TSharedPtr(InValue, InValue.Get()) { }

	/** Constructs a TSharedPtr which shares ownership of the array managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE TSharedPtr(const TSharedRef<U>& InValue) : TSharedPtr(InValue, InValue.Get()) { }

	/** Constructs a TSharedPtr which shares ownership of the array managed by 'InValue'. */
	FORCEINLINE TSharedPtr(TSharedPtr&& InValue) : TSharedPtr(MoveTemp(InValue), InValue.Get()) { }

	/** Constructs a TSharedPtr which shares ownership of the array managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE TSharedPtr(TSharedPtr<U>&& InValue) : TSharedPtr(MoveTemp(InValue), InValue.Get()) { }

	/** Constructs a TSharedPtr which gets ownership of the array managed by 'InValue'. */
	template <typename U, typename E> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U> && (CDestructible<E> || CLValueReference<E>))
	FORCEINLINE TSharedPtr(TUniquePtr<U, E>&& InValue) : TSharedPtr(InValue.Release(), Forward<E>(InValue.GetDeleter())) { }

	/** If this owns an array and it is the last TSharedPtr owning it, the array is destroyed through the owned deleter. */
	FORCEINLINE ~TSharedPtr() { if (Controller != nullptr) Controller->ReleaseSharedReference(); }

	/** Replaces the managed array with the one managed by 'InValue'. */
	FORCEINLINE TSharedPtr& operator=(const TSharedPtr& InValue) { return Helper::CopySharedReference(*this, InValue); }

	/** Replaces the managed array with the one managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE TSharedPtr& operator=(const TSharedPtr<U>& InValue) { return Helper::CopySharedReference(*this, InValue); }

	/** Replaces the managed array with the one managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE TSharedPtr& operator=(const TSharedRef<U>& InValue) { return Helper::CopySharedReference(*this, InValue); }

	/** Replaces the managed array with the one managed by 'InValue'. */
	FORCEINLINE TSharedPtr& operator=(TSharedPtr&& InValue) { return Helper::MoveSharedReference(*this, MoveTemp(InValue)); }

	/** Replaces the managed array with the one managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE TSharedPtr& operator=(TSharedPtr<U>&& InValue) { return Helper::MoveSharedReference(*this, MoveTemp(InValue)); }

	/** Replaces the managed array with the one managed by 'InValue'. */
	template <typename U, typename E> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U> && (CDestructible<E> || CLValueReference<E>))
	FORCEINLINE TSharedPtr& operator=(TUniquePtr<U, E>&& InValue) { return Swap(*this, TSharedPtr(MoveTemp(InValue))); }

	/** Effectively the same as calling Reset(). */
	FORCEINLINE TSharedPtr& operator=(nullptr_t) { Reset(); return *this; }

	/** Compares the pointer values of two TSharedPtr. */
	NODISCARD friend FORCEINLINE constexpr bool operator==(const TSharedPtr& LHS, const TSharedPtr& RHS) { return LHS.Get() == RHS.Get(); }

	/** Compares the pointer values of two TSharedPtr. */
	NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const TSharedPtr& LHS, const TSharedPtr& RHS) { return LHS.Get() <=> RHS.Get(); }

	/** Compares the pointer values with a raw pointer. */
	template <typename U = T*> requires (CNullPointer<U> || (CPointer<U> && CConvertibleTo<TRemovePointer<U>(*)[], T(*)[]>))
	NODISCARD FORCEINLINE constexpr bool operator==(U InPtr) const& { return Get() == InPtr; }

	/** Compares the pointer values with a raw pointer. */
	template <typename U = T*> requires (CNullPointer<U> || (CPointer<U> && CConvertibleTo<TRemovePointer<U>(*)[], T(*)[]>))
	NODISCARD FORCEINLINE constexpr strong_ordering operator<=>(U InPtr) const& { return Get() <=> InPtr; }

	/** Converts a shared pointer to a shared reference. The pointer MUST be valid or an assertion will trigger. */
	FORCEINLINE TSharedRef<T[]> ToSharedRef() const&
	{
		checkf(IsValid(), TEXT("TSharedRef cannot be initialized by nullptr."));
		return TSharedRef<T[]>(NAMESPACE_PRIVATE::SharedPtrConstructor, *this);
	}

	/** Converts a shared pointer to a shared reference. The pointer MUST be valid or an assertion will trigger. */
	FORCEINLINE TSharedRef<T[]> ToSharedRef() &&
	{
		checkf(IsValid(), TEXT("TSharedRef cannot be initialized by nullptr."));
		return TSharedRef<T[]>(NAMESPACE_PRIVATE::SharedPtrConstructor, *this);
	}

	/** Replaces the managed array. */
	template <typename U = T*> requires (CNullPointer<U> || (CPointer<U> && CConvertibleTo<TRemovePointer<U>(*)[], T(*)[]>))
	FORCEINLINE void Reset(U InPtr = nullptr) { *this = MoveTemp(TSharedPtr(InPtr)); }

	/** Replaces the managed array with a deleter. */
	template <typename U, typename E> requires (CNullPointer<U> || (CPointer<U> && CConvertibleTo<TRemovePointer<U>(*)[], T(*)[]>)
		&& CConstructibleFrom<TDecay<E>, E> && CInvocable<TDecay<E>, TRemoveExtent<T>*> && CDestructible<TDecay<E>>)
	FORCEINLINE void Reset(U InPtr, E&& InDeleter) { *this = MoveTemp(TSharedPtr(InPtr, Forward<E>(InDeleter))); }

	/** @return The pointer to the managed array. */
	NODISCARD FORCEINLINE constexpr T* Get() const { return Pointer; }

	/** @return The pointer to the owned deleter or nullptr. */
	template <typename E> requires (CInvocable<TDecay<E>, TRemoveExtent<T>*> && (CDestructible<E> || CLValueReference<E>))
	NODISCARD FORCEINLINE E* GetDeleter() const
	{
		const auto ControllerWithDeleter = dynamic_cast<NAMESPACE_PRIVATE::TSharedControllerWithDeleter<T, E>*>(Controller);
		return ControllerWithDeleter != nullptr ? &ControllerWithDeleter->GetDeleter() : nullptr;
	}

	/** @return true if *this owns an array, false otherwise. */
	NODISCARD FORCEINLINE constexpr bool           IsValid() const { return Get() != nullptr; }
	NODISCARD FORCEINLINE constexpr explicit operator bool() const { return Get() != nullptr; }

	/** @return The element at index, i.e. Get()[Index]. */
	NODISCARD FORCEINLINE constexpr T& operator[](size_t Index) const { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return Get()[Index]; }

	/**
	 * Returns the number of shared references to this array (including this reference.)
	 * IMPORTANT: With multi-threading this is only an estimate.
	 *
	 * @return The number of instances managing the current array.
	 */
	NODISCARD FORCEINLINE size_t GetSharedReferenceCount() const { return Controller != nullptr ? Controller->GetSharedReferenceCount() : 0; }

	/**
	 * Checks if this is the only instance managing the current array, i.e. whether GetSharedReferenceCount() == 1.
	 * IMPORTANT: With multi-threading this is only an estimate.
	 *
	 * @return true if *this is the only shared_ptr instance managing the current array, false otherwise.
	 */
	NODISCARD FORCEINLINE bool IsUnique() const { return GetSharedReferenceCount() == 1; }

	/**
	 * Checks whether this TSharedPtr precedes other in implementation defined owner-based (as opposed to value-based) order.
	 * This ensures that the ordering of TSharedPtr constructed by the aliasing constructor is not affected by array pointer.
	 *
	 * @return The ordering of the addresses of the control blocks.
	 */
	template <typename U = T> NODISCARD FORCEINLINE strong_ordering OwnerCompare(const TSharedRef<U>& InValue) const { return Controller <=> InValue.Controller; }
	template <typename U = T> NODISCARD FORCEINLINE strong_ordering OwnerCompare(const TSharedPtr<U>& InValue) const { return Controller <=> InValue.Controller; }

	/** Overloads the GetTypeHash algorithm for TSharedPtr. */
	NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TSharedPtr& A) { return GetTypeHash(A.Get()); }

	/** Overloads the Swap algorithm for TSharedPtr. */
	friend FORCEINLINE constexpr void Swap(TSharedPtr& A, TSharedPtr& B)
	{
		Swap(A.Pointer, B.Pointer);
		Swap(A.Controller, B.Controller);
	}

private:

	T* Pointer;
	NAMESPACE_PRIVATE::FSharedController* Controller;

	FORCEINLINE TSharedPtr(const TWeakPtr<T[]>& InValue)
	{
		const bool bIsUnexpired = InValue.Controller != nullptr && InValue.Controller->AddSharedReferenceIfUnexpired();

		Pointer    = bIsUnexpired ? InValue.Pointer    : nullptr;
		Controller = bIsUnexpired ? InValue.Controller : nullptr;
	
	}

	FORCEINLINE TSharedPtr(T* InPtr, NAMESPACE_PRIVATE::FSharedController* InController)
		: Pointer(InPtr), Controller(InController)
	{
		check(!((Pointer == nullptr) ^ (Controller == nullptr)));
	}

	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TSharedRef;
	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TSharedPtr;
	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TWeakPtr;

	template <typename U> friend class NAMESPACE_PRIVATE::TSharedProxy;

	friend struct NAMESPACE_PRIVATE::FSharedHelper;

};

/** TWeakPtr is a smart pointer that holds a non-owning ("weak") reference to an object that is managed by shared pointer. */
template <typename T> requires (CObject<T> && !CBoundedArray<T>)
class TWeakPtr final
{
private:

	using Helper = NAMESPACE_PRIVATE::FSharedHelper;

public:

	using ElementType = T;

	/** Constructs an empty TWeakPtr */
	FORCEINLINE constexpr TWeakPtr() : Pointer(nullptr), Controller(nullptr) { }

	/** Constructs an empty TWeakPtr */
	FORCEINLINE constexpr TWeakPtr(nullptr_t) : TWeakPtr() { }

	/** Constructs new TWeakPtr which shares an object managed by 'InValue'. */
	FORCEINLINE TWeakPtr(const TWeakPtr& InValue) : Pointer(InValue.Pointer), Controller(InValue.Controller)
	{
		if (Controller != nullptr)
		{
			Controller->AddWeakReference();
		}
	}

	/** Constructs new TWeakPtr which shares an object managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE constexpr TWeakPtr(const TWeakPtr<U>& InValue) : Pointer(InValue.Pointer), Controller(InValue.Controller)
	{
		if (Controller != nullptr)
		{
			Controller->AddWeakReference();
		}
	}

	/** Move constructors. Moves a TWeakPtr instance from 'InValue' into this. */
	FORCEINLINE TWeakPtr(TWeakPtr&& InValue) : Pointer(Exchange(InValue.Pointer, nullptr)), Controller(Exchange(InValue.Controller, nullptr)) { }

	/** Move constructors. Moves a TWeakPtr instance from 'InValue' into this. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE constexpr TWeakPtr(TWeakPtr<U>&& InValue) : Pointer(Exchange(InValue.Pointer, nullptr)), Controller(Exchange(InValue.Controller, nullptr)) { }
	
	/** Constructs a weak pointer from a shared reference. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE constexpr TWeakPtr(const TSharedRef<U>& InValue) : Pointer(InValue.Pointer), Controller(InValue.Controller)
	{
		Controller->AddWeakReference();
	}

	/** Constructs a weak pointer from a shared pointer. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE constexpr TWeakPtr(const TSharedPtr<U>& InValue) : Pointer(InValue.Pointer), Controller(InValue.Controller)
	{
		if (Controller != nullptr)
		{
			Controller->AddWeakReference();
		}
	}

	/** Destroys the TWeakPtr object. Results in no effect to the managed object. */
	FORCEINLINE ~TWeakPtr() { if (Controller != nullptr) Controller->ReleaseWeakReference(); }

	/** Replaces the managed object with the one managed by 'InValue'. */
	FORCEINLINE TWeakPtr& operator=(const TWeakPtr& InValue) { return Helper::CopyWeakReference(*this, InValue); }

	/** Replaces the managed object with the one managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE TWeakPtr& operator=(const TWeakPtr<U>& InValue) { return Helper::CopyWeakReference(*this, InValue); }

	/** Replaces the managed object with the one managed by 'InValue'. */
	FORCEINLINE TWeakPtr& operator=(TWeakPtr&& InValue) { return Helper::MoveWeakReference(*this, MoveTemp(InValue)); }

	/** Replaces the managed object with the one managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE TWeakPtr& operator=(TWeakPtr<U>&& InValue) { return Helper::MoveWeakReference(*this, MoveTemp(InValue)); }

	/** Assignment operator sets this weak pointer from a shared reference. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE TWeakPtr& operator=(const TSharedRef<U>& InValue) { return Helper::CopyWeakReference(*this, InValue); }

	/** Assignment operator sets this weak pointer from a shared pointer. */
	template <typename U> requires (CConvertibleTo<U*, T*> && !CArray<U>)
	FORCEINLINE TWeakPtr& operator=(const TSharedPtr<U>& InValue) { return Helper::CopyWeakReference(*this, InValue); }

	/** Effectively the same as calling Reset(). */
	FORCEINLINE TWeakPtr& operator=(nullptr_t) { Reset(); return *this; }

	/** Resets this weak pointer, removing a weak reference to the object. */
	FORCEINLINE void Reset()
	{
		if (Controller != nullptr)
		{
			Controller->ReleaseWeakReference();
			Pointer    = nullptr;
			Controller = nullptr;
		}
	}

	/**
	 * Returns the number of shared references to this object (Excluding this reference.)
	 * IMPORTANT: With multi-threading this is only an estimate.
	 *
	 * @return The number of instances managing the current object.
	 */
	NODISCARD FORCEINLINE size_t GetSharedReferenceCount() const { return Controller != nullptr ? Controller->GetSharedReferenceCount() : 0; }

	/** @return true if the weak pointer is expired and a lock operator would have failed. */
	NODISCARD FORCEINLINE bool Expired() const { return GetSharedReferenceCount() == 0; }

	/** A TSharedPtr which shares ownership of the owned object if Expired() returns false. Else returns nullptr. */
	NODISCARD FORCEINLINE TSharedPtr<T> Lock() const { return *this; }

	/** Overloads the Swap algorithm for TWeakPtr. */
	friend FORCEINLINE constexpr void Swap(TWeakPtr& A, TWeakPtr& B)
	{
		Swap(A.Pointer, B.Pointer);
		Swap(A.Controller, B.Controller);
	}

private:

	T* Pointer;
	NAMESPACE_PRIVATE::FSharedController* Controller;

	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TSharedRef;
	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TSharedPtr;
	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TWeakPtr;

	friend struct NAMESPACE_PRIVATE::FSharedHelper;

};

/** TWeakPtr is a smart pointer that holds a non-owning ("weak") reference to an array that is managed by shared pointer. */
template <typename T>
class TWeakPtr<T[]> final
{
private:

	using Helper = NAMESPACE_PRIVATE::FSharedHelper;

public:

	using ElementType = T;

	/** Constructs an empty TWeakPtr */
	FORCEINLINE constexpr TWeakPtr() : Pointer(nullptr), Controller(nullptr) { }

	/** Constructs an empty TWeakPtr */
	FORCEINLINE constexpr TWeakPtr(nullptr_t) : TWeakPtr() { }

	/** Constructs new TWeakPtr which shares an array managed by 'InValue'. */
	FORCEINLINE TWeakPtr(const TWeakPtr& InValue) : Pointer(InValue.Pointer), Controller(InValue.Controller)
	{
		if (Controller != nullptr)
		{
			Controller->AddWeakReference();
		}
	}

	/** Constructs new TWeakPtr which shares an array managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE constexpr TWeakPtr(const TWeakPtr<U>& InValue) : Pointer(InValue.Pointer), Controller(InValue.Controller)
	{
		if (Controller != nullptr)
		{
			Controller->AddWeakReference();
		}
	}

	/** Move constructors. Moves a TWeakPtr instance from 'InValue' into this. */
	FORCEINLINE TWeakPtr(TWeakPtr&& InValue) : Pointer(Exchange(InValue.Pointer, nullptr)), Controller(Exchange(InValue.Controller, nullptr)) { }

	/** Move constructors. Moves a TWeakPtr instance from 'InValue' into this. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE constexpr TWeakPtr(TWeakPtr<U>&& InValue) : Pointer(Exchange(InValue.Pointer, nullptr)), Controller(Exchange(InValue.Controller, nullptr)) { }

	/** Constructs a weak pointer from a shared reference. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE constexpr TWeakPtr(const TSharedRef<U>& InValue) : Pointer(InValue.Pointer), Controller(InValue.Controller)
	{
		Controller->AddWeakReference();
	}

	/** Constructs a weak pointer from a shared pointer. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE constexpr TWeakPtr(const TSharedPtr<U>& InValue) : Pointer(InValue.Pointer), Controller(InValue.Controller)
	{
		if (Controller != nullptr)
		{
			Controller->AddWeakReference();
		}
	}

	/** Destroys the TWeakPtr array. Results in no effect to the managed array. */
	FORCEINLINE ~TWeakPtr() { if (Controller != nullptr) Controller->ReleaseWeakReference(); }

	/** Replaces the managed array with the one managed by 'InValue'. */
	FORCEINLINE TWeakPtr& operator=(const TWeakPtr& InValue) { return Helper::CopyWeakReference(*this, InValue); }

	/** Replaces the managed array with the one managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE TWeakPtr& operator=(const TWeakPtr<U>& InValue) { return Helper::CopyWeakReference(*this, InValue); }

	/** Replaces the managed array with the one managed by 'InValue'. */
	FORCEINLINE TWeakPtr& operator=(TWeakPtr&& InValue) { return Helper::MoveWeakReference(*this, MoveTemp(InValue)); }

	/** Replaces the managed array with the one managed by 'InValue'. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE TWeakPtr& operator=(TWeakPtr<U>&& InValue) { return Helper::MoveWeakReference(*this, MoveTemp(InValue)); }

	/** Assignment operator sets this weak pointer from a shared reference. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE TWeakPtr& operator=(const TSharedRef<U>& InValue) { return Helper::CopyWeakReference(*this, InValue); }

	/** Assignment operator sets this weak pointer from a shared pointer. */
	template <typename U> requires (CConvertibleTo<TRemoveExtent<U>(*)[], T(*)[]> && CArray<U>)
	FORCEINLINE TWeakPtr& operator=(const TSharedPtr<U>& InValue) { return Helper::CopyWeakReference(*this, InValue); }

	/** Effectively the same as calling Reset(). */
	FORCEINLINE TWeakPtr& operator=(nullptr_t) { Reset(); return *this; }

	/** Resets this weak pointer, removing a weak reference to the array. */
	FORCEINLINE void Reset()
	{
		if (Controller != nullptr)
		{
			Controller->ReleaseWeakReference();
			Pointer    = nullptr;
			Controller = nullptr;
		}
	}

	/**
	 * Returns the number of shared references to this array (Excluding this reference.)
	 * IMPORTANT: With multi-threading this is only an estimate.
	 *
	 * @return The number of instances managing the current array.
	 */
	NODISCARD FORCEINLINE size_t GetSharedReferenceCount() const { return Controller != nullptr ? Controller->GetSharedReferenceCount() : 0; }

	/** @return true if the weak pointer is expired and a lock operator would have failed. */
	NODISCARD FORCEINLINE bool Expired() const { return GetSharedReferenceCount() == 0; }

	/** A TSharedPtr which shares ownership of the owned array if Expired() returns false. Else returns nullptr. */
	NODISCARD FORCEINLINE TSharedPtr<T[]> Lock() const { return *this; }

	/** Overloads the Swap algorithm for TWeakPtr. */
	friend FORCEINLINE constexpr void Swap(TWeakPtr& A, TWeakPtr& B)
	{
		Swap(A.Pointer, B.Pointer);
		Swap(A.Controller, B.Controller);
	}

private:

	T* Pointer;
	NAMESPACE_PRIVATE::FSharedController* Controller;

	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TSharedRef;
	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TSharedPtr;
	template <typename U> requires (CObject<U> && !CBoundedArray<U>) friend class TWeakPtr;

	friend struct NAMESPACE_PRIVATE::FSharedHelper;

};

/** Constructs an object of type T and wraps it in a TSharedRef or TSharedPtr. Without initialization. */
template <typename T> requires (CObject<T> && !CArray<T> && !CConstructibleFrom<T, FNoInit> && CDestructible<T>)
NODISCARD FORCEINLINE NAMESPACE_PRIVATE::TSharedProxy<T> MakeShared(FNoInit)
{
	const auto Controller = new NAMESPACE_PRIVATE::TSharedControllerWithObject<T>(NoInit);
	return NAMESPACE_PRIVATE::TSharedProxy<T>(Controller->GetPointer(), Controller);
}

/** Constructs an object of type T and wraps it in a TSharedRef or TSharedPtr. */
template <typename T, typename... Ts> requires (CObject<T> && !CArray<T> && CConstructibleFrom<T, Ts...> && CDestructible<T>)
NODISCARD FORCEINLINE NAMESPACE_PRIVATE::TSharedProxy<T> MakeShared(Ts&&... Args)
{
	const auto Controller = new NAMESPACE_PRIVATE::TSharedControllerWithObject<T>(Forward<Ts>(Args)...);
	return NAMESPACE_PRIVATE::TSharedProxy<T>(Controller->GetPointer(), Controller);
}

/** Constructs an array of type T and wraps it in a TSharedRef or TSharedPtr. Without initialization. */
template <typename T> requires (CUnboundedArray<T> && CDefaultConstructible<TRemoveExtent<T>> && CDestructible<TRemoveExtent<T>>)
NODISCARD FORCEINLINE NAMESPACE_PRIVATE::TSharedProxy<T> MakeShared(size_t N, FNoInit)
{
	const auto Controller = NAMESPACE_PRIVATE::TSharedControllerWithArray<TRemoveExtent<T>>::New(N, NoInit);
	return NAMESPACE_PRIVATE::TSharedProxy<T>(Controller->GetPointer(), Controller);
}

/** Constructs an array of type T and wraps it in a TSharedRef or TSharedPtr. */
template <typename T> requires (CUnboundedArray<T> && CDefaultConstructible<TRemoveExtent<T>> && CDestructible<TRemoveExtent<T>>)
NODISCARD FORCEINLINE NAMESPACE_PRIVATE::TSharedProxy<T> MakeShared(size_t N)
{
	const auto Controller = NAMESPACE_PRIVATE::TSharedControllerWithArray<TRemoveExtent<T>>::New(N);
	return NAMESPACE_PRIVATE::TSharedProxy<T>(Controller->GetPointer(), Controller);
}

/** Construction of arrays of known bound is disallowed. */
template <typename T, typename... Ts> requires (CBoundedArray<T>)
void MakeShared(Ts&&...) = delete;

/** Creates a new instance of TSharedRef whose stored pointer is obtained from stored pointer of 'InValue' using a static_cast expression. */
template <typename T, typename U> requires (requires(U* InPtr) { static_cast<T*>(InPtr); })
NODISCARD FORCEINLINE TSharedRef<T> StaticCast(const TSharedRef<U>& InValue)
{
	return TSharedRef<T>(InValue, static_cast<T*>(InValue.Get()));
}

/** Creates a new instance of TSharedRef whose stored pointer is obtained from stored pointer of 'InValue' using a static_cast expression. */
template <typename T, typename U> requires (requires(U* InPtr) { static_cast<T*>(InPtr); })
NODISCARD FORCEINLINE TSharedRef<T> StaticCast(TSharedRef<U>&& InValue)
{
	return TSharedRef<T>(MoveTemp(InValue), static_cast<T*>(InValue.Get()));
}

/** Creates a new instance of TSharedRef whose stored pointer is obtained from stored pointer of 'InValue' using a dynamic_cast expression. */
template <typename T, typename U> requires (requires(U* InPtr) { dynamic_cast<T*>(InPtr); })
NODISCARD FORCEINLINE TSharedRef<T> DynamicCast(const TSharedRef<U>& InValue)
{
	return TSharedRef<T>(InValue, dynamic_cast<T*>(InValue.Get()));
}

/** Creates a new instance of TSharedRef whose stored pointer is obtained from stored pointer of 'InValue' using a dynamic_cast expression. */
template <typename T, typename U> requires (requires(U* InPtr) { dynamic_cast<T*>(InPtr); })
NODISCARD FORCEINLINE TSharedRef<T> DynamicCast(TSharedRef<U>&& InValue)
{
	return TSharedRef<T>(MoveTemp(InValue), dynamic_cast<T*>(InValue.Get()));
}

/** Creates a new instance of TSharedRef whose stored pointer is obtained from stored pointer of 'InValue' using a const_cast expression. */
template <typename T, typename U> requires (requires(U* InPtr) { const_cast<T*>(InPtr); })
NODISCARD FORCEINLINE TSharedRef<T> ConstCast(const TSharedRef<U>& InValue)
{
	return TSharedRef<T>(InValue, const_cast<T*>(InValue.Get()));
}

/** Creates a new instance of TSharedRef whose stored pointer is obtained from stored pointer of 'InValue' using a const_cast expression. */
template <typename T, typename U> requires (requires(U* InPtr) { const_cast<T*>(InPtr); })
NODISCARD FORCEINLINE TSharedRef<T> ConstCast(TSharedRef<U>&& InValue)
{
	return TSharedRef<T>(MoveTemp(InValue), const_cast<T*>(InValue.Get()));
}

/** Creates a new instance of TSharedRef whose stored pointer is obtained from stored pointer of 'InValue' using a reinterpret_cast expression. */
template <typename T, typename U> requires (requires(U* InPtr) { reinterpret_cast<T*>(InPtr); })
NODISCARD FORCEINLINE TSharedRef<T> ReinterpretCast(const TSharedRef<U>& InValue)
{
	return TSharedRef<T>(InValue, reinterpret_cast<T*>(InValue.Get()));
}

/** Creates a new instance of TSharedRef whose stored pointer is obtained from stored pointer of 'InValue' using a reinterpret_cast expression. */
template <typename T, typename U> requires (requires(U* InPtr) { reinterpret_cast<T*>(InPtr); })
NODISCARD FORCEINLINE TSharedRef<T> ReinterpretCast(TSharedRef<U>&& InValue)
{
	return TSharedRef<T>(MoveTemp(InValue), reinterpret_cast<T*>(InValue.Get()));
}

/** Creates a new instance of TSharedPtr whose stored pointer is obtained from stored pointer of 'InValue' using a static_cast expression. */
template <typename T, typename U> requires (requires(U* InPtr) { static_cast<T*>(InPtr); })
NODISCARD FORCEINLINE TSharedPtr<T> StaticCast(const TSharedPtr<U>& InValue)
{
	return TSharedPtr<T>(InValue, static_cast<T*>(InValue.Get()));
}

/** Creates a new instance of TSharedPtr whose stored pointer is obtained from stored pointer of 'InValue' using a static_cast expression. */
template <typename T, typename U> requires (requires(U* InPtr) { static_cast<T*>(InPtr); })
NODISCARD FORCEINLINE TSharedPtr<T> StaticCast(TSharedPtr<U>&& InValue)
{
	return TSharedPtr<T>(MoveTemp(InValue), static_cast<T*>(InValue.Get()));
}

/** Creates a new instance of TSharedPtr whose stored pointer is obtained from stored pointer of 'InValue' using a dynamic_cast expression. */
template <typename T, typename U> requires (requires(U* InPtr) { dynamic_cast<T*>(InPtr); })
NODISCARD FORCEINLINE TSharedPtr<T> DynamicCast(const TSharedPtr<U>& InValue)
{
	return TSharedPtr<T>(InValue, dynamic_cast<T*>(InValue.Get()));
}

/** Creates a new instance of TSharedPtr whose stored pointer is obtained from stored pointer of 'InValue' using a dynamic_cast expression. */
template <typename T, typename U> requires (requires(U* InPtr) { dynamic_cast<T*>(InPtr); })
NODISCARD FORCEINLINE TSharedPtr<T> DynamicCast(TSharedPtr<U>&& InValue)
{
	return TSharedPtr<T>(MoveTemp(InValue), dynamic_cast<T*>(InValue.Get()));
}

/** Creates a new instance of TSharedPtr whose stored pointer is obtained from stored pointer of 'InValue' using a const_cast expression. */
template <typename T, typename U> requires (requires(U* InPtr) { const_cast<T*>(InPtr); })
NODISCARD FORCEINLINE TSharedPtr<T> ConstCast(const TSharedPtr<U>& InValue)
{
	return TSharedPtr<T>(InValue, const_cast<T*>(InValue.Get()));
}

/** Creates a new instance of TSharedPtr whose stored pointer is obtained from stored pointer of 'InValue' using a const_cast expression. */
template <typename T, typename U> requires (requires(U* InPtr) { const_cast<T*>(InPtr); })
NODISCARD FORCEINLINE TSharedPtr<T> ConstCast(TSharedPtr<U>&& InValue)
{
	return TSharedPtr<T>(MoveTemp(InValue), const_cast<T*>(InValue.Get()));
}

/** Creates a new instance of TSharedPtr whose stored pointer is obtained from stored pointer of 'InValue' using a reinterpret_cast expression. */
template <typename T, typename U> requires (requires(U* InPtr) { reinterpret_cast<T*>(InPtr); })
NODISCARD FORCEINLINE TSharedPtr<T> ReinterpretCast(const TSharedPtr<U>& InValue)
{
	return TSharedPtr<T>(InValue, reinterpret_cast<T*>(InValue.Get()));
}

/** Creates a new instance of TSharedPtr whose stored pointer is obtained from stored pointer of 'InValue' using a reinterpret_cast expression. */
template <typename T, typename U> requires (requires(U* InPtr) { reinterpret_cast<T*>(InPtr); })
NODISCARD FORCEINLINE TSharedPtr<T> ReinterpretCast(TSharedPtr<U>&& InValue)
{
	return TSharedPtr<T>(MoveTemp(InValue), reinterpret_cast<T*>(InValue.Get()));
}

DEFINE_TPointerTraits(TSharedRef);
DEFINE_TPointerTraits(TSharedPtr);

NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END