// Copyright (c) 2024 Codemongous LLC. All Rights Reserved.

#pragma once

#include "ISCW_FField.h"
#include "ISCW_UEdGraphPin.h"
#include "ISCW_UObject.h"

#include <memory>

namespace std
{
	using namespace Sourcify;
	template<typename T>
	struct less<shared_ptr<T>>
	{
		bool operator()(const shared_ptr<T>& Lhs, const shared_ptr<T>& Rhs) const
		{
			if (!Lhs || !Rhs)
			{
				return Lhs < Rhs;
			}
			if constexpr (std::is_base_of_v<ISCW_UObject, T> or std::is_base_of_v<ISCW_FField, T> or std::is_base_of_v<ISCW_UEdGraphPin, T>)
			{
				return Lhs->Less(*Rhs);
			}
			else
			{
				return Lhs < Rhs;
			}
		}
	};

	template<typename T>
	struct equal_to<shared_ptr<T>>
	{
		bool operator()(const shared_ptr<T>& Lhs, const shared_ptr<T>& Rhs) const
		{
			if (!Lhs || !Rhs)
			{
				return Lhs == Rhs;
			}
			if constexpr (std::is_base_of_v<ISCW_UObject, T> or std::is_base_of_v<ISCW_FField, T> or std::is_base_of_v<ISCW_UEdGraphPin, T>)
			{
				return Lhs->ISCW_Equals(*Rhs);
			}
			else
			{
				return Lhs == Rhs;
			}
		}
	};
#if _HAS_CXX20
	template<typename T>
	concept SCW_HashableWrapper = requires(T Wrapper)
	{
		std::is_base_of_v<ISCW_UObject, T> or std::is_base_of_v<ISCW_FField, T> or std::is_base_of_v<ISCW_UEdGraphPin, T>;
		{ Wrapper.GetHash() } -> std::convertible_to<size_t>;
	};

	template<SCW_HashableWrapper T>
	struct hash<T>
	{
		size_t operator()(const T& Ptr) const
		{
			return Ptr.GetHash();
		}
	};

	template<typename T>
	concept SCW_EqualityComparable = requires(T Lhs, T Rhs)
	{
		std::is_base_of_v<ISCW_UObject, T> or std::is_base_of_v<ISCW_FField, T> or std::is_base_of_v<ISCW_UEdGraphPin, T>;
		{ Lhs.ISCW_Equals(Rhs) } -> std::convertible_to<bool>;
	};

	template<SCW_EqualityComparable T, SCW_EqualityComparable U>
	inline bool operator==(const shared_ptr<T>& Lhs, const shared_ptr<U>& Rhs)
	{
		return Lhs ? Lhs->ISCW_Equals(Rhs) : Rhs == nullptr;
	}

	template<SCW_EqualityComparable T, SCW_EqualityComparable U>
	inline bool operator!=(const shared_ptr<T>& Lhs, const shared_ptr<U>& Rhs)
	{
		return !(Lhs == Rhs);
	}

	template<SCW_EqualityComparable T>
	inline bool operator==(const shared_ptr<T>& Lhs, nullptr_t)
	{
		return Lhs.get() == nullptr;
	}

	template<SCW_EqualityComparable T>
	inline bool operator!=(const shared_ptr<T>& Lhs, nullptr_t)
	{
		return Lhs.get() != nullptr;
	}

	template<SCW_EqualityComparable T>
	inline bool operator==(nullptr_t, const shared_ptr<T>& Rhs)
	{
		return Rhs.get() == nullptr;
	}

	template<SCW_EqualityComparable T>
	inline bool operator!=(nullptr_t, const shared_ptr<T>& Rhs)
	{
		return Rhs.get() != nullptr;
	}

	template<SCW_EqualityComparable T, SCW_EqualityComparable U>
	inline bool operator==(const T& Lhs, const U& Rhs)
	{
		return Lhs.ISCW_Equals(Rhs);
	}

	template<SCW_EqualityComparable T, SCW_EqualityComparable U>
	inline bool operator!=(const T& Lhs, const U& Rhs)
	{
		return !(Lhs == Rhs);
	}
#endif
} // namespace std