
#include "SimplygonBlueprintLibrary.h"
#include "StandinUtilitiesModule.h"
#include "LODRecipeContext.h"
#include "LODRecipe.h"
#include "SimplygonAssetUserData.h"
#include "Engine/StaticMeshActor.h"

DEFINE_LOG_CATEGORY_STATIC( LogSimplygonBlueprintLibrary, Verbose, All );
#define LOCTEXT_NAMESPACE "SimplygonBlueprintLibrary"

static bool ContainsMetaTag( AActor* InActor, ESimplygonMetaTagType InMetaTag )
{
	
	TInlineComponentArray<UStaticMeshComponent*> Components;
	InActor->GetComponents( Components );

	for( auto& Component : Components )
	{
		USimplygonAssetUserData* SimplygonUserData = Component->GetAssetUserData<USimplygonAssetUserData>();
		if( SimplygonUserData )
		{
			if( SimplygonUserData->MetaTag == InMetaTag )
			{
				return true;
			}
		}
	}
	return false;
}

USimplygonBPFunctionLibrary::USimplygonBPFunctionLibrary( const FObjectInitializer& ObjectInitializer )
	: Super(ObjectInitializer)
{ }


bool USimplygonBPFunctionLibrary::BuildStandinMeshes()
{
	IStandinUtilitiesModule::Get().BuildStandinMeshActors(false);
	return false;
}


AStandInMeshActor* USimplygonBPFunctionLibrary::CreateStandinActorFromActors( const TArray<AActor*>& InActors )
{
	
	AStandInMeshActor* Result = IStandinUtilitiesModule::Get().CreateStandinMeshActor(InActors);

	return Result;
}


void USimplygonBPFunctionLibrary::AddActorToStandinActor( AStandInMeshActor* InStandin, AActor* InActor )
{
	if( !InStandin || !InActor )
	{
		UE_LOG( LogSimplygonBlueprintLibrary, Warning, TEXT( "InStandin or InActor was null." ));
		return;
	}

	IStandinUtilitiesModule::Get().AddActorToStandinActor(InActor, InStandin);
}


void USimplygonBPFunctionLibrary::AddActorsToStandinActor( AStandInMeshActor* InStandin, const TArray<AActor*>& InActors )
{
	if( !InStandin )
	{
		UE_LOG( LogSimplygonBlueprintLibrary, Warning, TEXT( "InStandin was null." ) );
		return;
	}


	IStandinUtilitiesModule::Get().AddActorsToStandinActor(InStandin, InActors);
}


void USimplygonBPFunctionLibrary::RemoveActorFromStandinActor( AActor* InActor )
{
	if( !InActor )
	{
		UE_LOG( LogSimplygonBlueprintLibrary, Warning, TEXT( "InActor was null." ) );
		return;
	}

	IStandinUtilitiesModule::Get().RemoveActorFromStandinActor(InActor);
}

void USimplygonBPFunctionLibrary::RemoveActorsFromStandinActor( AStandInMeshActor* InStandin, const TArray<AActor*>& InActors )
{
	if( !InStandin )
	{
		UE_LOG( LogSimplygonBlueprintLibrary, Warning, TEXT( "InStandin was null." ) );
		return;
	}
		
	IStandinUtilitiesModule::Get().RemoveActorsFromStandinActor(InStandin, InActors);
}



void USimplygonBPFunctionLibrary::BuildAllLODRecipes()
{
	FLODRecipeManager::Get().BuildAll(  );
}

void USimplygonBPFunctionLibrary::BuildLODRecipe( TSoftObjectPtr<UObject> Mesh )
{
	if( !Mesh.IsValid() )
	{
		UE_LOG( LogSimplygonBlueprintLibrary, Warning, TEXT( "Mesh not a valid pointer." ) );
		return;
	}

	FLODRecipeManager::Get().Build( Mesh );
}


void USimplygonBPFunctionLibrary::BuildLODRecipesOnlyForStaticMeshes( const TArray<class ULODRecipe*>& LODRecipes )
{
	FLODRecipeContext InContext;
	InContext.FilterCriteria = ELODRecipeFilterCriteria::StaticMeshes;
	FLODRecipeManager::Get().Build( LODRecipes, InContext );
}

void USimplygonBPFunctionLibrary::BuildLODRecipesOnlyForSkeletalMeshes( const TArray<class ULODRecipe*>& LODRecipes )
{
	FLODRecipeContext InContext;
	InContext.FilterCriteria = ELODRecipeFilterCriteria::SkeletalMeshes;
	FLODRecipeManager::Get().Build( LODRecipes, InContext );
}

void USimplygonBPFunctionLibrary::AssignRecipeToAsset( ULODRecipe* InRecipe, UObject* InAsset )
{
	if( !InRecipe || !InAsset )
	{
		UE_LOG( LogSimplygonBlueprintLibrary, Warning, TEXT( "InRecipe or InAsset was null." ) );
		return;
	}

	InRecipe->AddMesh( InAsset );
}


bool USimplygonBPFunctionLibrary::IsClippingGeometry( AActor* InActor )
{
	if( !InActor || ! InActor->IsA<AStaticMeshActor>() )
	{
		UE_LOG( LogSimplygonBlueprintLibrary, Warning, TEXT( "InActor was null or not a valid StaticMeshActor." ) );
		return false;
	}
		

	return ContainsMetaTag( InActor, ESimplygonMetaTagType::ClippingGeometry );

}

bool USimplygonBPFunctionLibrary::IsOccludingGeometry( AActor* InActor )
{
	if (!InActor || !InActor->IsA<AStaticMeshActor>()) {

		UE_LOG( LogSimplygonBlueprintLibrary, Warning, TEXT( "InActor was null or not a valid StaticMeshActor." ) );
		return false;
	}
		

	return ContainsMetaTag( InActor, ESimplygonMetaTagType::Occluder );
}


void USimplygonBPFunctionLibrary::AddSimplygonUserData( AActor* InActor, ESimplygonMetaTagType InMetaTag )
{
	if (!InActor) 
	{
		UE_LOG( LogSimplygonBlueprintLibrary, Warning, TEXT( "InActor was null" ) );
		return;
	}

	TInlineComponentArray<UStaticMeshComponent*> Components;
	InActor->GetComponents( Components );

	for( auto& Component : Components ) 
	{
		USimplygonAssetUserData* SimplygonUserData = Component->GetAssetUserData<USimplygonAssetUserData>();
		if( !SimplygonUserData )
		{
			SimplygonUserData = NewObject<USimplygonAssetUserData>( Component, USimplygonAssetUserData::StaticClass() );
			SimplygonUserData->SetFlags( RF_Transactional );
			SimplygonUserData->MetaTag = InMetaTag;
			InActor->GetRootComponent()->AddAssetUserData( SimplygonUserData );
			Component->AddAssetUserData( SimplygonUserData );
		}
		else
		{
			SimplygonUserData->MetaTag = InMetaTag;
		}
	}
}


void USimplygonBPFunctionLibrary::RemoveSimplygonUserData( AActor* InActor )
{
	if( !InActor || !InActor->IsA<AStaticMeshActor>() )
		return;

	TInlineComponentArray<UStaticMeshComponent*> Components;
	InActor->GetComponents( Components );

	for( auto& Component : Components )
	{
		USimplygonAssetUserData* SimplygonUserData = Component->GetAssetUserData<USimplygonAssetUserData>();
		if( SimplygonUserData ) 
		{
			Component->RemoveUserDataOfClass( USimplygonAssetUserData::StaticClass() );
		}
	}
	
}

#undef LOCTEXT_NAMESPACE