/*
DOCUMENTATION

// CONFIG

Override default asset
#define DELEGATE_ASSERT(expression, ...)

Override default static_assert
#define DELEGATE_STATIC_ASSERT(expression, msg)

Set inline allocator size (default: 32)
#define DELEGATE_INLINE_ALLOCATION_SIZE

Reassign allocation functions:
Delegates::SetAllocationCallbacks(allocFunction, freeFunc);


// USAGE

## Classes ##
- ```Delegate<RetVal, Args>```
- ```MulticastDelegate<Args>```

## Features ##
- Support for:
	- Static/Global methods
	- Member functions
	- Lambda's
	- std::shared_ptr
- Delegate object is allocated inline if it is under 32 bytes
- Add payload to delegate during bind-time
- Move operations enable optimization

## Example Usage ##

### Delegate ###

Delegate<int, float> del;
del.BindLambda([](float a, int payload)
{
	std::cout << "Lambda delegate parameter: " << a << std::endl;
	std::cout << "Lambda delegate payload: " << payload << std::endl;
	return 10;
}, 50);
std::cout << "Lambda delegate return value: " << del.Execute(20) << std::endl;

Output:
Lambda delegate parameter: 20
Lambda delegate payload: 50
Lambda delegate return value: 10

### MulticastDelegate ###

struct Foo
{
	void Bar(float a, int payload)
	{
		std::cout << "Raw delegate parameter: " << a << std::endl;
		std::cout << "Raw delegate payload: " << payload << std::endl;
	}
};
MulticastDelegate<float> del;
del.AddLambda([](float a, int payload)
{
	std::cout << "Lambda delegate parameter: " << a << std::endl;
	std::cout << "Lambda delegate payload: " << payload << std::endl;
}, 90);

Foo foo;
del.AddRaw(&foo, &Foo::Bar, 10);
del.Broadcast(20);

Output:
Lambda delegate parameter: 20
Lambda delegate payload: 90
Raw delegate parameter: 20
Raw delegate payload: 10

*/

#ifndef CPP_DELEGATES
#define CPP_DELEGATES

#include <vector>
#include <memory>
#include <tuple>
#include "extern.h"

///////////////////////////////////////////////////////////////
//////////////////// DEFINES SECTION //////////////////////////
///////////////////////////////////////////////////////////////

#ifndef DELEGATE_ASSERT
#include <assert.h>
#define DELEGATE_ASSERT(expression, ...) assert(expression)
#endif

#ifndef DELEGATE_STATIC_ASSERT
#if __cplusplus <= 199711L
#define DELEGATE_STATIC_ASSERT(expression, msg) static_assert(expression, msg)
#else
#define DELEGATE_STATIC_ASSERT(expression, msg)
#endif
#endif

//The allocation size of delegate data.
//Delegates larger than this will be heap allocated.
#ifndef DELEGATE_INLINE_ALLOCATION_SIZE
#define DELEGATE_INLINE_ALLOCATION_SIZE 32
#endif

#define DECLARE_DELEGATE(name, ...) \
using name = Delegate<void, __VA_ARGS__>

#define DECLARE_DELEGATE_RET(name, retValue, ...) \
using name = Delegate<retValue, __VA_ARGS__>

#define DECLARE_MULTICAST_DELEGATE(name, ...) \
using name = MulticastDelegate<__VA_ARGS__>; \
using name ## Delegate = MulticastDelegate<__VA_ARGS__>::DelegateT

#define DECLARE_EVENT(name, ownerType, ...) \
class name : public MulticastDelegate<__VA_ARGS__> \
{ \
private: \
	friend class ownerType; \
	using MulticastDelegate::Broadcast; \
	using MulticastDelegate::RemoveAll; \
	using MulticastDelegate::Remove; \
};

///////////////////////////////////////////////////////////////
/////////////////// INTERNAL SECTION //////////////////////////
///////////////////////////////////////////////////////////////

#if __cplusplus >= 201703L
#define NO_DISCARD [[nodiscard]]
#else
#define NO_DISCARD		
#endif

namespace _DelegatesInteral
{
	template<bool IsConst, typename Object, typename RetVal, typename ...Args>
	struct MemberFunction;

	template<typename Object, typename RetVal, typename ...Args>
	struct MemberFunction<true, Object, RetVal, Args...>
	{
		using Type = RetVal(Object::*)(Args...) const;
	};

	template<typename Object, typename RetVal, typename ...Args>
	struct MemberFunction<false, Object, RetVal, Args...>
	{
		using Type = RetVal(Object::*)(Args...);
	};

	static void* (*Alloc)(size_t size) = [](size_t size) { return malloc(size); };
	static void(*Free)(void* pPtr) = [](void* pPtr) { free(pPtr); };
	template<typename T>
	void DelegateDeleteFunc(T* pPtr)
	{
		pPtr->~T();
		DelegateFreeFunc(pPtr);
	}
}

namespace Delegates
{
	using AllocateCallback = void* (*)(size_t size);
	using FreeCallback = void(*)(void* pPtr);
	inline void SetAllocationCallbacks(AllocateCallback allocateCallback, FreeCallback freeCallback)
	{
		_DelegatesInteral::Alloc = allocateCallback;
		_DelegatesInteral::Free = freeCallback;
	}
}

class i_delegate_base
{
public:
	i_delegate_base() = default;
	virtual ~i_delegate_base() noexcept = default;
	virtual const void* get_owner() const { return nullptr; }
	virtual void clone(void* pDestination) = 0;
};

//Base type for delegates
template<typename RetVal, typename... Args>
class i_delegate : public i_delegate_base
{
public:
	virtual RetVal execute(Args&&... args) = 0;
};

template<typename RetVal, typename... Args2>
class static_delegate;

template<typename RetVal, typename... Args, typename... Args2>
class static_delegate<RetVal(Args...), Args2...> : public i_delegate<RetVal, Args...>
{
public:
	using DelegateFunction = RetVal(*)(Args..., Args2...);

	static_delegate(DelegateFunction function, Args2&&... payload)
		: function_(function), payload_(std::forward<Args2>(payload)...)
	{}

	static_delegate(DelegateFunction function, const std::tuple<Args2...>& payload)
		: function_(function), payload_(payload)
	{}

	virtual RetVal execute(Args&&... args) override
	{
		return execute_internal(std::forward<Args>(args)..., std::index_sequence_for<Args2...>());
	}

	virtual void clone(void* pDestination) override
	{
		new (pDestination) static_delegate(function_, payload_);
	}

private:
	template<std::size_t... Is>
	RetVal execute_internal(Args&&... args, std::index_sequence<Is...>)
	{
		return function_(std::forward<Args>(args)..., std::get<Is>(payload_)...);
	}

	DelegateFunction function_;
	std::tuple<Args2...> payload_;
};

template<bool IsConst, typename T, typename RetVal, typename... Args2>
class raw_delegate;

template<bool IsConst, typename T, typename RetVal, typename... Args, typename... Args2>
class raw_delegate<IsConst, T, RetVal(Args...), Args2...> : public i_delegate<RetVal, Args...>
{
public:
	using DelegateFunction = typename _DelegatesInteral::MemberFunction<IsConst, T, RetVal, Args..., Args2...>::Type;

	raw_delegate(T* pObject, DelegateFunction function, Args2&&... payload)
		: object_(pObject), function_(function), payload_(std::forward<Args2>(payload)...)
	{}

	raw_delegate(T* pObject, DelegateFunction function, const std::tuple<Args2...>& payload)
		: object_(pObject), function_(function), payload_(payload)
	{}

	virtual RetVal execute(Args&&... args) override
	{
		return execute_internal(std::forward<Args>(args)..., std::index_sequence_for<Args2...>());
	}
	virtual const void* get_owner() const override
	{
		return object_;
	}

	virtual void clone(void* pDestination) override
	{
		new (pDestination) raw_delegate(object_, function_, payload_);
	}

private:
	template<std::size_t... Is>
	RetVal execute_internal(Args&&... args, std::index_sequence<Is...>)
	{
		return (object_->*function_)(std::forward<Args>(args)..., std::get<Is>(payload_)...);
	}

	T* object_;
	DelegateFunction function_;
	std::tuple<Args2...> payload_;
};

template<typename TLambda, typename RetVal, typename... Args>
class lambda_delegate;

template<typename TLambda, typename RetVal, typename... Args, typename... Args2>
class lambda_delegate<TLambda, RetVal(Args...), Args2...> : public i_delegate<RetVal, Args...>
{
public:
	explicit lambda_delegate(TLambda&& lambda, Args2&&... payload)
		: lambda_(std::forward<TLambda>(lambda)),
		payload_(std::forward<Args2>(payload)...)
	{}

	explicit lambda_delegate(const TLambda& lambda, const std::tuple<Args2...>& payload)
		: lambda_(lambda),
		payload_(payload)
	{}

	RetVal execute(Args&&... args) override
	{
		return execute_internal(std::forward<Args>(args)..., std::index_sequence_for<Args2...>());
	}

	virtual void clone(void* pDestination) override
	{
		new (pDestination) lambda_delegate(lambda_, payload_);
	}

private:
	template<std::size_t... Is>
	RetVal execute_internal(Args&&... args, std::index_sequence<Is...>)
	{
		return (RetVal)((lambda_)(std::forward<Args>(args)..., std::get<Is>(payload_)...));
	}

	TLambda lambda_;
	std::tuple<Args2...> payload_;
};

template<bool IsConst, typename T, typename RetVal, typename... Args>
class sp_delegate;

template<bool IsConst, typename RetVal, typename T, typename... Args, typename... Args2>
class sp_delegate<IsConst, T, RetVal(Args...), Args2...> : public i_delegate<RetVal, Args...>
{
public:
	using DelegateFunction = typename _DelegatesInteral::MemberFunction<IsConst, T, RetVal, Args..., Args2...>::Type;

	sp_delegate(std::shared_ptr<T> pObject, DelegateFunction pFunction, Args2&&... payload)
		: object_(pObject),
		function_(pFunction),
		payload_(std::forward<Args2>(payload)...)
	{}

	sp_delegate(std::weak_ptr<T> pObject, DelegateFunction pFunction, const std::tuple<Args2...>& payload)
		: object_(pObject),
		function_(pFunction),
		payload_(payload)
	{}

	virtual RetVal execute(Args&&... args) override
	{
		return execute_internal(std::forward<Args>(args)..., std::index_sequence_for<Args2...>());
	}

	virtual const void* get_owner() const override
	{
		return object_.expired() ? nullptr : object_.lock().get();
	}

	virtual void clone(void* pDestination) override
	{
		new (pDestination) sp_delegate(object_, function_, payload_);
	}

private:
	template<std::size_t... Is>
	RetVal execute_internal(Args&&... args, std::index_sequence<Is...>)
	{
		if (object_.expired())
		{
			return RetVal();
		}
		else
		{
			std::shared_ptr<T> pPinned = object_.lock();
			return (pPinned.get()->*function_)(std::forward<Args>(args)..., std::get<Is>(payload_)...);
		}
	}

	std::weak_ptr<T> object_;
	DelegateFunction function_;
	std::tuple<Args2...> payload_;
};

//A handle to a delegate used for a multicast delegate
//Static ID so that every handle is unique
class delegate_handle
{
public:
	constexpr delegate_handle() noexcept
		: id_(INVALID_ID)
	{
	}

	explicit delegate_handle(bool /*generateId*/) noexcept
		: id_(get_new_id())
	{
	}

	~delegate_handle() noexcept = default;
	delegate_handle(const delegate_handle& other) = default;
	delegate_handle& operator=(const delegate_handle& other) = default;

	delegate_handle(delegate_handle&& other) noexcept
		: id_(other.id_)
	{
		other.reset();
	}

	delegate_handle& operator=(delegate_handle&& other) noexcept
	{
		id_ = other.id_;
		other.reset();
		return *this;
	}

	operator bool() const noexcept
	{
		return is_valid();
	}

	bool operator==(const delegate_handle& other) const noexcept
	{
		return id_ == other.id_;
	}

	bool operator<(const delegate_handle& other) const noexcept
	{
		return id_ < other.id_;
	}

	bool is_valid() const noexcept
	{
		return id_ != INVALID_ID;
	}

	void reset() noexcept
	{
		id_ = INVALID_ID;
	}

	constexpr static const unsigned int INVALID_ID = (unsigned int)~0;
private:
	unsigned int id_;
	CORE_API static unsigned int CURRENT_ID;

	static int get_new_id()
	{
		unsigned int output = delegate_handle::CURRENT_ID++;
		if (delegate_handle::CURRENT_ID == INVALID_ID)
		{
			delegate_handle::CURRENT_ID = 0;
		}
		return output;
	}
};

template<size_t MaxStackSize>
class inline_allocator
{
public:
	//Constructor
	constexpr inline_allocator() noexcept
		: size_(0)
	{
		DELEGATE_STATIC_ASSERT(MaxStackSize > sizeof(void*), "MaxStackSize is smaller or equal to the size of a pointer. This will make the use of an InlineAllocator pointless. Please increase the MaxStackSize.");
	}

	//Destructor
	~inline_allocator() noexcept
	{
		free();
	}

	//Copy constructor
	inline_allocator(const inline_allocator& other)
		: size_(0)
	{
		if (other.has_allocation())
		{
			memcpy(allocate(other.size_), other.get_allocation(), other.size_);
		}
		size_ = other.size_;
	}

	//Copy assignment operator
	inline_allocator& operator=(const inline_allocator& other)
	{
		if (other.has_allocation())
		{
			memcpy(allocate(other.size_), other.get_allocation(), other.size_);
		}
		size_ = other.size_;
		return *this;
	}

	//Move constructor
	inline_allocator(inline_allocator&& other) noexcept
		: size_(other.size_)
	{
		other.size_ = 0;
		if (size_ > MaxStackSize)
		{
			std::swap(pPtr, other.pPtr);
		}
		else
		{
			memcpy(Buffer, other.Buffer, size_);
		}
	}

	//Move assignment operator
	inline_allocator& operator=(inline_allocator&& other) noexcept
	{
		free();
		size_ = other.size_;
		other.size_ = 0;
		if (size_ > MaxStackSize)
		{
			std::swap(pPtr, other.pPtr);
		}
		else
		{
			memcpy(Buffer, other.Buffer, size_);
		}
		return *this;
	}

	//Allocate memory of given size
	//If the size is over the predefined threshold, it will be allocated on the heap
	void* allocate(const size_t size)
	{
		if (size_ != size)
		{
			free();
			size_ = size;
			if (size > MaxStackSize)
			{
				pPtr = _DelegatesInteral::Alloc(size);
				return pPtr;
			}
		}
		return (void*)Buffer;
	}

	//Free the allocated memory
	void free()
	{
		if (size_ > MaxStackSize)
		{
			_DelegatesInteral::Free(pPtr);
		}
		size_ = 0;
	}

	//Return the allocated memory either on the stack or on the heap
	void* get_allocation() const
	{
		if (has_allocation())
		{
			return has_heap_allocation() ? pPtr : (void*)Buffer;
		}
		else
		{
			return nullptr;
		}
	}

	size_t get_size() const
	{
		return size_;
	}

	bool has_allocation() const
	{
		return size_ > 0;
	}

	bool has_heap_allocation() const
	{
		return size_ > MaxStackSize;
	}

private:
	//If the allocation is smaller than the threshold, Buffer is used
	//Otherwise pPtr is used together with a separate dynamic allocation
	union
	{
		char Buffer[MaxStackSize];
		void* pPtr;
	};
	size_t size_;
};

class delegate_base
{
public:
	//Default constructor
	constexpr delegate_base() noexcept
		: allocator_()
	{}

	//Default destructor
	virtual ~delegate_base() noexcept
	{
		release();
	}

	//Copy contructor
	delegate_base(const delegate_base& other)
	{
		if (other.allocator_.has_allocation())
		{
			allocator_.allocate(other.allocator_.get_size());
			other.get_delegate()->clone(allocator_.get_allocation());
		}
	}

	//Copy assignment operator
	delegate_base& operator=(const delegate_base& other)
	{
		release();
		if (other.allocator_.has_allocation())
		{
			allocator_.allocate(other.allocator_.get_size());
			other.get_delegate()->clone(allocator_.get_allocation());
		}
		return *this;
	}

	//Move constructor
	delegate_base(delegate_base&& other) noexcept
		: allocator_(std::move(other.allocator_))
	{}

	//Move assignment operator
	delegate_base& operator=(delegate_base&& other) noexcept
	{
		release();
		allocator_ = std::move(other.allocator_);
		return *this;
	}

	//Gets the owner of the deletage
	//Only valid for SPDelegate and RawDelegate.
	//Otherwise returns nullptr by default
	const void* get_owner() const
	{
		if (allocator_.has_allocation())
		{
			return get_delegate()->get_owner();
		}
		return nullptr;
	}

	size_t get_size() const
	{
		return allocator_.get_size();
	}

	//Clear the bound delegate if it is bound to the given object.
	//Ignored when pObject is a nullptr
	void clear_if_bound_to(void* pObject)
	{
		if (pObject != nullptr && is_bound_to(pObject))
		{
			release();
		}
	}

	//Clear the bound delegate if it exists
	void clear()
	{
		release();
	}

	//If the allocator has a size, it means it's bound to something
	bool is_bound() const
	{
		return allocator_.has_allocation();
	}

	bool is_bound_to(void* pObject) const
	{
		if (pObject == nullptr || allocator_.has_allocation() == false)
		{
			return false;
		}
		return get_delegate()->get_owner() == pObject;
	}

protected:
	void release()
	{
		if (allocator_.has_allocation())
		{
			get_delegate()->~i_delegate_base();
			allocator_.free();
		}
	}

	i_delegate_base* get_delegate() const
	{
		return static_cast<i_delegate_base*>(allocator_.get_allocation());
	}

	//Allocator for the delegate itself.
	//Delegate gets allocated when its is smaller or equal than 64 bytes in size.
	//Can be changed by preference
	inline_allocator<DELEGATE_INLINE_ALLOCATION_SIZE> allocator_;
};

//Delegate that can be bound to by just ONE object
template<typename RetVal, typename... Args>
class delegate : public delegate_base
{
private:
	template<typename T, typename... Args2>
	using ConstMemberFunction = typename _DelegatesInteral::MemberFunction<true, T, RetVal, Args..., Args2...>::Type;
	template<typename T, typename... Args2>
	using NonConstMemberFunction = typename _DelegatesInteral::MemberFunction<false, T, RetVal, Args..., Args2...>::Type;

public:
	using IDelegateT = i_delegate<RetVal, Args...>;

	//Create delegate using member function
	template<typename T, typename... Args2>
	NO_DISCARD static delegate create_raw(T* pObj, NonConstMemberFunction<T, Args2...> pFunction, Args2... args)
	{
		delegate handler;
		handler.bind<raw_delegate<false, T, RetVal(Args...), Args2...>>(pObj, pFunction, std::forward<Args2>(args)...);
		return handler;
	}

	template<typename T, typename... Args2>
	NO_DISCARD static delegate create_raw(T* pObj, ConstMemberFunction<T, Args2...> pFunction, Args2... args)
	{
		delegate handler;
		handler.bind<raw_delegate<true, T, RetVal(Args...), Args2...>>(pObj, pFunction, std::forward<Args2>(args)...);
		return handler;
	}

	//Create delegate using global/static function
	template<typename... Args2>
	NO_DISCARD static delegate create_static(RetVal(*pFunction)(Args..., Args2...), Args2... args)
	{
		delegate handler;
		handler.bind<static_delegate<RetVal(Args...), Args2...>>(pFunction, std::forward<Args2>(args)...);
		return handler;
	}

	//Create delegate using std::shared_ptr
	template<typename T, typename... Args2>
	NO_DISCARD static delegate create_sp(const std::shared_ptr<T>& pObject, NonConstMemberFunction<T, Args2...> pFunction, Args2... args)
	{
		delegate handler;
		handler.bind<sp_delegate<false, T, RetVal(Args...), Args2...>>(pObject, pFunction, std::forward<Args2>(args)...);
		return handler;
	}

	template<typename T, typename... Args2>
	NO_DISCARD static delegate create_sp(const std::shared_ptr<T>& pObject, ConstMemberFunction<T, Args2...> pFunction, Args2... args)
	{
		delegate handler;
		handler.bind<sp_delegate<true, T, RetVal(Args...), Args2...>>(pObject, pFunction, std::forward<Args2>(args)...);
		return handler;
	}

	//Create delegate using a lambda
	template<typename TLambda, typename... Args2>
	NO_DISCARD static delegate create_lambda(TLambda&& lambda, Args2... args)
	{
		delegate handler;
		handler.bind<lambda_delegate<TLambda, RetVal(Args...), Args2...>>(std::forward<TLambda>(lambda), std::forward<Args2>(args)...);
		return handler;
	}

	//Bind a member function
	template<typename T, typename... Args2>
	void bind_raw(T* pObject, NonConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
	{
		DELEGATE_STATIC_ASSERT(!std::is_const<T>::value, "Cannot bind a non-const function on a const object");
		*this = create_raw<T, Args2... >(pObject, pFunction, std::forward<Args2>(args)...);
	}

	template<typename T, typename... Args2>
	void bind_raw(T* pObject, ConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
	{
		*this = create_raw<T, Args2... >(pObject, pFunction, std::forward<Args2>(args)...);
	}

	//Bind a static/global function
	template<typename... Args2>
	void bind_static(RetVal(*pFunction)(Args..., Args2...), Args2&&... args)
	{
		*this = create_static<Args2... >(pFunction, std::forward<Args2>(args)...);
	}

	//Bind a lambda
	template<typename LambdaType, typename... Args2>
	void bind_lambda(LambdaType&& lambda, Args2&&... args)
	{
		*this = create_lambda<LambdaType, Args2... >(std::forward<LambdaType>(lambda), std::forward<Args2>(args)...);
	}

	//Bind a member function with a shared_ptr object
	template<typename T, typename... Args2>
	void bind_sp(std::shared_ptr<T> pObject, NonConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
	{
		DELEGATE_STATIC_ASSERT(!std::is_const<T>::value, "Cannot bind a non-const function on a const object");
		*this = create_sp<T, Args2... >(pObject, pFunction, std::forward<Args2>(args)...);
	}

	template<typename T, typename... Args2>
	void bind_sp(std::shared_ptr<T> pObject, ConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
	{
		*this = create_sp<T, Args2... >(pObject, pFunction, std::forward<Args2>(args)...);
	}

	//Execute the delegate with the given parameters
	RetVal execute(Args... args) const
	{
		DELEGATE_ASSERT(allocator_.has_allocation(), "Delegate is not bound");
		return ((IDelegateT*)get_delegate())->execute(std::forward<Args>(args)...);
	}

	RetVal execute_if_bound(Args... args) const
	{
		if (is_bound())
		{
			return ((IDelegateT*)get_delegate())->execute(std::forward<Args>(args)...);
		}
		return RetVal();
	}

private:
	template<typename T, typename... Args3>
	void bind(Args3&&... args)
	{
		release();
		void* pAlloc = allocator_.allocate(sizeof(T));
		new (pAlloc) T(std::forward<Args3>(args)...);
	}
};

//Delegate that can be bound to by MULTIPLE objects
template<typename... Args>
class multicast_delegate : public delegate_base
{
public:
	using DelegateT = delegate<void, Args...>;

private:
	struct delegate_handler_pair
	{
		delegate_handle handle;
		DelegateT callback;
		delegate_handler_pair() : handle(false) {}
		delegate_handler_pair(const delegate_handle& handle, const DelegateT& callback) : handle(handle), callback(callback) {}
		delegate_handler_pair(const delegate_handle& handle, DelegateT&& callback) : handle(handle), callback(std::move(callback)) {}
	};
	template<typename T, typename... Args2>
	using ConstMemberFunction = typename _DelegatesInteral::MemberFunction<true, T, void, Args..., Args2...>::Type;
	template<typename T, typename... Args2>
	using NonConstMemberFunction = typename _DelegatesInteral::MemberFunction<false, T, void, Args..., Args2...>::Type;

public:
	//Default constructor
	constexpr multicast_delegate()
		: locks_(0)
	{
	}

	//Default destructor
	~multicast_delegate() noexcept = default;

	//Default copy constructor
	multicast_delegate(const multicast_delegate& other) = default;

	//Defaul copy assignment operator
	multicast_delegate& operator=(const multicast_delegate& other) = default;

	//Move constructor
	multicast_delegate(multicast_delegate&& other) noexcept
		: events_(std::move(other.events_)),
		locks_(std::move(other.locks_))
	{
	}

	//Move assignment operator
	multicast_delegate& operator=(multicast_delegate&& other) noexcept
	{
		events_ = std::move(other.events_);
		locks_ = std::move(other.locks_);
		return *this;
	}

	template<typename T>
	delegate_handle operator+=(T&& l)
	{
		return add(DelegateT::create_lambda(std::move(l)));
	}

	//Add delegate with the += operator
	delegate_handle operator+=(DelegateT&& handler) noexcept
	{
		return add(std::forward<DelegateT>(handler));
	}

	//Remove a delegate using its DelegateHandle
	bool operator-=(delegate_handle& handle)
	{
		return remove(handle);
	}

	delegate_handle add(DelegateT&& handler) noexcept
	{
		//Favour an empty space over a possible array reallocation
		for (size_t i = 0; i < events_.size(); ++i)
		{
			if (events_[i].handle.is_valid() == false)
			{
				events_[i] = delegate_handler_pair(delegate_handle(true), std::move(handler));
				return events_[i].handle;
			}
		}
		events_.emplace_back(delegate_handle(true), std::move(handler));
		return events_.back().handle;
	}

	//Bind a member function
	template<typename T, typename... Args2>
	delegate_handle add_raw(T* pObject, NonConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
	{
		return add(DelegateT::create_raw(pObject, pFunction, std::forward<Args2>(args)...));
	}

	template<typename T, typename... Args2>
	delegate_handle add_raw(T* pObject, ConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
	{
		return add(DelegateT::create_raw(pObject, pFunction, std::forward<Args2>(args)...));
	}

	//Bind a static/global function
	template<typename... Args2>
	delegate_handle add_static(void(*pFunction)(Args..., Args2...), Args2&&... args)
	{
		return add(DelegateT::create_static(pFunction, std::forward<Args2>(args)...));
	}

	//Bind a lambda
	template<typename LambdaType, typename... Args2>
	delegate_handle add_lambda(LambdaType&& lambda, Args2&&... args)
	{
		return add(DelegateT::create_lambda(std::forward<LambdaType>(lambda), std::forward<Args2>(args)...));
	}

	//Bind a member function with a shared_ptr object
	template<typename T, typename... Args2>
	delegate_handle add_sp(std::shared_ptr<T> pObject, NonConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
	{
		return add(DelegateT::create_sp(pObject, pFunction, std::forward<Args2>(args)...));
	}

	template<typename T, typename... Args2>
	delegate_handle add_sp(std::shared_ptr<T> pObject, ConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
	{
		return add(DelegateT::create_sp(pObject, pFunction, std::forward<Args2>(args)...));
	}

	//Removes all handles that are bound from a specific object
	//Ignored when pObject is null
	//Note: Only works on Raw and SP bindings
	void remove_object(void* pObject)
	{
		if (pObject != nullptr)
		{
			for (size_t i = 0; i < events_.size(); ++i)
			{
				if (events_[i].callback.get_owner() == pObject)
				{
					if (is_locked())
					{
						events_[i].callback.clear();
					}
					else
					{
						std::swap(events_[i], events_[events_.size() - 1]);
						events_.pop_back();
					}
				}
			}
		}
	}

	//Remove a function from the event list by the handle
	bool remove(delegate_handle& handle)
	{
		if (handle.is_valid())
		{
			for (size_t i = 0; i < events_.size(); ++i)
			{
				if (events_[i].handle == handle)
				{
					if (is_locked())
					{
						events_[i].callback.clear();
					}
					else
					{
						std::swap(events_[i], events_[events_.size() - 1]);
						events_.pop_back();
					}
					handle.reset();
					return true;
				}
			}
		}
		return false;
	}

	bool is_bound_to(const delegate_handle& handle) const
	{
		if (handle.is_valid())
		{
			for (size_t i = 0; i < events_.size(); ++i)
			{
				if (events_[i].handle == handle)
				{
					return true;
				}
			}
		}
		return false;
	}

	//Remove all the functions bound to the delegate
	void remove_all()
	{
		if (is_locked())
		{
			for (delegate_handler_pair& handler : events_)
			{
				handler.callback.clear();
			}
		}
		else
		{
			events_.clear();
		}
	}

	void compress(const size_t maxSpace = 0)
	{
		if (is_locked() == false)
		{
			size_t toDelete = 0;
			for (size_t i = 0; i < events_.size() - toDelete; ++i)
			{
				if (events_[i].handle.is_valid() == false)
				{
					std::swap(events_[i], events_[toDelete]);
					++toDelete;
				}
			}
			if (toDelete > maxSpace)
			{
				events_.resize(events_.size() - toDelete);
			}
		}
	}

	//Execute all functions that are bound
	void broadcast(Args ...args)
	{
		lock();
		for (size_t i = 0; i < events_.size(); ++i)
		{
			if (events_[i].handle.is_valid())
			{
				events_[i].callback.execute(std::forward<Args>(args)...);
			}
		}
		unlock();
	}

	size_t get_size() const
	{
		return events_.size();
	}

private:
	void lock()
	{
		++locks_;
	}

	void unlock()
	{
		//Unlock() should never be called more than Lock()!
		DELEGATE_ASSERT(locks_ > 0);
		--locks_;
	}

	//Returns true is the delegate is currently broadcasting
	//If this is true, the order of the array should not be changed otherwise this causes undefined behaviour
	bool is_locked() const
	{
		return locks_ > 0;
	}

	std::vector<delegate_handler_pair> events_;
	unsigned int locks_;
};

#endif