// Based somewhat on The Impossibly Fast C++ delegate, a quicker funciton
// wrapper than std::funciton / native* delegates.
#pragma once
// Unused, allows T1(T2, T3...) syntax with a specialisation
template<typename T> class Delegate;
// Specialisation, allows for Delegate<ReturnType(ArgType1, ArgType2...)>
// syntax e.g void return delegate with float parameter:
// Delegate<void(float)> ADelegate;
template<typename R, typename... Args>
class Delegate <R(Args...)> final {
public:
	// Create a delegate from an instance method. Usage:
	// Delegate<void(float)>::Create<Type, &Type::MethodName>(pointerToInstance)
	template<typename T, R(T::*TMethod)(Args...)>
	static Delegate Create(T* instance) {
		return Delegate(instance, instanceMethodRunner<T, TMethod>);
	}
	// Create a delegate from a const instance method. Usage:
	// Delegate<void(float)>::Create<Type, &Type::MethodName>(pointerToInstance)
	template<typename T, R(T::*TMethod)(Args...) const>
	static Delegate Create(T const* instance) {
		return Delegate(const_cast<T*>(instance), constInstanceMethodRunner<T, TMethod>);
	}
	// Create a delegate from a global function. Usage:
	// Delegate<void(float)>::Create<&MethodName>()
	template<R(*TMethod)(Args...)>
	static Delegate Create() {
		return Delegate(nullptr, globalRunner<TMethod>);
	}
	// Create a Delegate from a lambda expression. Usage:
	// Delegate<void(float)>::Create( /* lambda expression */ )
	template<typename L>
	static Delegate Create(const L& instance) {
		return Delegate((void*)(&instance), lambdaRunner<L>);
	}

	Delegate& operator=(const Delegate& other) {
		object = other.object;
		runner = other.runner;
		return *this;
	}

	// Run the function stored by this delegate.
	R operator () (Args... args) const {
		return (*runner)(object, args...);
	}

	bool operator == (const Delegate& other) const {
		return runner == other.runner && object == other.object;
	}
	bool operator != (const Delegate& other) const {
		return !(this == other);
	}
private:
	using FunctionRunner = R(*)(void* this_ptr, Args...);

	void* object = nullptr;
	FunctionRunner runner = nullptr;

	Delegate(void* object, FunctionRunner stub) {
		this->object = object;
		this->runner = stub;
	}
	// Static methods to run each type of funciton.

	template<typename T, R(T::*TMethod)(Args...)>
	static R instanceMethodRunner(void* thisPtr, Args... args) {
		T* p = static_cast<T*>(thisPtr);
		return (p->*TMethod)(args...);
	}

	template<typename T, R(T::*TMethod)(Args...) const>
	static R constInstanceMethodRunner(void* thisPtr, Args... args) {
		T* const p = static_cast<T*>(thisPtr);
		return (p->*TMethod)(args...);
	}

	template<R(*TMethod)(Args...)>
	static R globalRunner(void* this_ptr, Args... args) {
		return (TMethod)(args...);
	}

	template<typename L>
	static R lambdaRunner(void* thisPointer, Args... args) {
		L* p = static_cast<L*>(thisPointer);
		return *p(args...);
	}
};
