#include "EmoShapeSubSystem/EmoShapeWebUtility/Public/EmoShapeLambdaRunnable.h"

#include "Async/Async.h"

FEmoShapeLatentAction* FEmoShapeLatentAction::CreateLatentAction(FLatentActionInfo& LatentInfo, UObject* WorldContext)
{
	UWorld* World = GEngine->GetWorldFromContextObject(WorldContext, EGetWorldErrorMode::LogAndReturnNull);
	if (!World) 
	{
		return nullptr;
	}
	FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
	FEmoShapeLatentAction *LatentAction = LatentActionManager.FindExistingAction<FEmoShapeLatentAction>(LatentInfo.CallbackTarget, LatentInfo.UUID);
	LatentAction = new FEmoShapeLatentAction(LatentInfo);	//safe to use new since latentactionmanager will delete it
	int32 UUID = LatentInfo.UUID;
	LatentAction->OnCancelNotification = [UUID]()
	{
		UE_LOG(LogTemp, Log, TEXT("%d graph callback cancelled."), UUID);
	};
	LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, LatentAction);
	return LatentAction;
}

FEmoShapeLatentAction::FEmoShapeLatentAction(const FLatentActionInfo& LatentInfo) :
ExecutionFunction(LatentInfo.ExecutionFunction),
	OutputLink(LatentInfo.Linkage),
	CallbackTarget(LatentInfo.CallbackTarget),
	Called(false)
{
	
}

void FEmoShapeLatentAction::UpdateOperation(FLatentResponse& Response)
{
	Response.FinishAndTriggerIf(Called, ExecutionFunction, OutputLink, CallbackTarget);
}

void FEmoShapeLatentAction::Call()
{
	Called = true;
}

void FEmoShapeLatentAction::Cancel()
{
	if (OnCancelNotification)
	{
		OnCancelNotification();
	}
}

void FEmoShapeLatentAction::NotifyObjectDestroyed()
{
	Cancel();
}

void FEmoShapeLatentAction::NotifyActionAborted()
{
	Cancel();
}

#if WITH_EDITOR
FString FEmoShapeLatentAction::GetDescription() const
{
	{
		if (Called)
		{
			return TEXT("Done.");
		}
		else
		{
			return TEXT("Pending.");
		}
	};
}
#endif

void FEmoShapeLambdaRunnable::RunLambdaOnBackGroundThread(TFunction<void()> InFunction)
{
	Async(EAsyncExecution::Thread, InFunction);
}

void FEmoShapeLambdaRunnable::RunLambdaOnBackGroundThreadPool(TFunction<void()> InFunction)
{
	Async(EAsyncExecution::ThreadPool, InFunction);
}

FGraphEventRef FEmoShapeLambdaRunnable::RunShortLambdaOnGameThread(TFunction<void()> InFunction)
{
	return FFunctionGraphTask::CreateAndDispatchWhenReady(InFunction, TStatId(), nullptr, ENamedThreads::GameThread);
}

FGraphEventRef FEmoShapeLambdaRunnable::RunShortLambdaOnBackGroundTask(TFunction<void()> InFunction)
{
	return FFunctionGraphTask::CreateAndDispatchWhenReady(InFunction, TStatId(), nullptr, ENamedThreads::AnyThread);
}

void FEmoShapeLambdaRunnable::SetTimeout(TFunction<void()> OnDone, float DurationInSec, bool bCallbackOnGameThread)
{
	RunLambdaOnBackGroundThread([OnDone, DurationInSec, bCallbackOnGameThread]()
	{
		FPlatformProcess::Sleep(DurationInSec);

		if (bCallbackOnGameThread)
		{
			RunShortLambdaOnGameThread(OnDone);
		}
		else
		{
			OnDone();
		}
	});
}
