#pragma once

#include "CoreMinimal.h"
#include "Engine/World.h"
#include "Engine/GameInstance.h"
#include "Engine/Engine.h"

class PORTAINTERFACE_API PortaUtils
{
public:
	static UWorld* GetCurrentWorld();

	template<typename T, typename... Args>
	static void CallWorldSubsystemMember(void (T::*Member)(Args...), Args... args)
	{
		auto Sub = GetWorldSubsystem<T>();
		if (Sub)
			(Sub->*Member)(args...);
	}

	template<typename T, typename... Args>
	static void CallGameInstanceSubsystemMember(void (T::* Member)(Args...), Args... args)
	{
		auto Sub = GetGameInstanceSubsystem<T>();
		if (Sub)
			(Sub->*Member)(args...);
	}

	template<typename T>
	struct Initializer
	{
		T t;
		Initializer() : t() {}
	};

	template<typename T, typename Ret, typename... Args>
	static Ret CallGameInstanceSubsystemMember(Ret (T::* Member)(Args...), Args... args)
	{
		auto Sub = GetGameInstanceSubsystem<T>();
		if (Sub)
			return (Sub->*Member)(args...);

		//some trickery to get this to compile. This code should probably never run.
		Initializer<Ret> ret;
		return ret.t;
	}

#if WITH_EDITOR
	template<typename T, typename... Args>
	static void CallEditorSubsystemMember(void (T::* Member)(Args...), Args... args)
	{
		auto Sub = GetEditorSubsystem<T>();
		if (Sub)
			(Sub->*Member)(args...);
	}


	template<typename T, typename Ret, typename... Args>
	static Ret CallEditorSubsystemMember(Ret(T::* Member)(Args...), Args... args)
	{
		auto Sub = GetEditorSubsystem<T>();
		if (Sub)
			return (Sub->*Member)(args...);

		//some trickery to get this to compile. This code should probably never run.
		Initializer<Ret> ret;
		return ret.t;
	}
#endif

	template<typename T, typename... Args>
	static void CallEngineSubsystemMember(void (T::* Member)(Args...), Args... args)
	{
		auto Sub = GetEngineSubsystem<T>();
		if (Sub)
			(Sub->*Member)(args...);
	}


	template<typename T, typename Ret, typename... Args>
	static Ret CallEngineSubsystemMember(Ret(T::* Member)(Args...), Args... args)
	{
		auto Sub = GetEngineSubsystem<T>();
		if (Sub)
			return (Sub->*Member)(args...);

		//some trickery to get this to compile. This code should probably never run.
		Initializer<Ret> ret;
		return ret.t;
	}

	template<typename T>
	static T* GetWorldSubsystem()
	{
		auto World = GetCurrentWorld();
		
		if (!World)
			return nullptr;

		return World->GetSubsystem<T>();
	}

	template<typename T>
	static T* GetGameInstanceSubsystem()
	{
		auto World = GetCurrentWorld();
		if (!World)
			return nullptr;

		auto GI = World->GetGameInstance();
		if (!GI)
			return nullptr;
		return GI->GetSubsystem<T>();
	}

	template<typename T>
	static T* GetEngineSubsystem()
	{
		return GEngine->GetEngineSubsystem<T>();
	}

	template<typename T>
	static T* GetEditorSubsystem()
	{
#if WITH_EDITOR
		return GEditor->GetEditorSubsystem<T>();
#else
		return nullptr;
#endif
	}
};
