// Copyright Epic Games, Inc. All Rights Reserved.

#include "EmoShapeSubSystem.h"

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

#define LOCTEXT_NAMESPACE "FEmoShapeSubSystemModule"

DEFINE_LOG_CATEGORY(LogEmoShape);

void FEmoShapeSubSystemModule::StartupModule()
{
	// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
}

void FEmoShapeSubSystemModule::ShutdownModule()
{
	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
	// we call this function before unloading the module.

	auto AllActivePointers = PluginNativePointers;
	for (auto& Pointer : AllActivePointers)
	{
		ReleaseNativePointer(Pointer);
	}
	AllActivePointers.Empty();

	//Wait for all pointers to release
	float Elapsed = 0.f;
	while (bHasActiveNativePointers)
	{
		FPlatformProcess::Sleep(0.01f);
		Elapsed += 0.01f;

		//if it takes more than 5 seconds, just quit
		if (Elapsed > 5.f)
		{
			UE_LOG(SocketIO, Warning, TEXT("FEmoShapeIOClientModule::ShutdownModule force quit due to long wait to quit."));
			break;
		}
	}

	//Native pointers will be automatically released by uninitialize components
	PluginNativePointers.Empty();
}

TSharedPtr<FESIONative> FEmoShapeSubSystemModule::NewValidNativePointer(const bool bShouldUseTlsLibraries,
	const bool bShouldSkipCertificateVerification)
{
	TSharedPtr<FESIONative> NewPointer = MakeShareable(new FESIONative(bShouldUseTlsLibraries, bShouldSkipCertificateVerification));
	
	PluginNativePointers.Add(NewPointer);
	
	bHasActiveNativePointers = true;

	return NewPointer;
}

TSharedPtr<FESIONative> FEmoShapeSubSystemModule::ValidSharedNativePointer(FString SharedId,
	const bool bShouldUseTlsLibraries, const bool bShouldSkipCertificateVerification)
{
	//Found key? return it
	if (SharedNativePointers.Contains(SharedId))
	{
		return SharedNativePointers[SharedId];
	}
	//Otherwise request a new id and return it
	else
	{
		TSharedPtr<FESIONative> NewNativePtr = NewValidNativePointer(bShouldUseTlsLibraries, bShouldSkipCertificateVerification);
		SharedNativePointers.Add(SharedId, NewNativePtr);
		AllSharedPtrs.Add(NewNativePtr);
		return NewNativePtr;
	}
}

void FEmoShapeSubSystemModule::ReleaseNativePointer(TSharedPtr<FESIONative> PointerToRelease)
{
	//Remove shared ptr references if any
	if (AllSharedPtrs.Contains(PointerToRelease))
	{
		AllSharedPtrs.Remove(PointerToRelease);
		for (auto& Pair : SharedNativePointers)
		{
			if (Pair.Value == PointerToRelease)
			{
				SharedNativePointers.Remove(Pair.Key);
				break;
			}
		}
	}

	//Release the pointer on the background thread pool, this can take ~ 1 sec per connection
	FEmoShapeLambdaRunnable::RunLambdaOnBackGroundThread([PointerToRelease, this]
	{
		if (PointerToRelease.IsValid())
		{
			//Ensure only one thread at a time removes from array 
			{
				FScopeLock Lock(&DeleteSection);
				PluginNativePointers.Remove(PointerToRelease);
			}

			//Disconnect, this can happen simultaneously
			if (PointerToRelease->bIsConnected)
			{
				PointerToRelease->SyncDisconnect();
			}

			//Update our active status
			bHasActiveNativePointers = PluginNativePointers.Num() > 0;
		}
	});
}

#undef LOCTEXT_NAMESPACE
	
IMPLEMENT_MODULE(FEmoShapeSubSystemModule, EmoShapeSubSystem)