//======================================================================================================
// Company Name:       MotionTechnologies.inc
// Copyright Year:     2025
// File:               MoALiveLinkPropComponent.cpp
//======================================================================================================

#include "MoALiveLinkPropComponent.h"
#include "ILiveLinkClient.h"
#include "LiveLinkClientReference.h"
#include "Roles/LiveLinkTransformRole.h"
#include "Roles/LiveLinkTransformTypes.h"
#include "GameFramework/Actor.h"
#include "Components/StaticMeshComponent.h"
#include "Engine/StaticMesh.h"
#include "UObject/ConstructorHelpers.h"

UMoALiveLinkPropComponent::UMoALiveLinkPropComponent()
{
	PrimaryComponentTick.bCanEverTick = true;
	PrimaryComponentTick.bStartWithTickEnabled = true;
	PrimaryComponentTick.TickGroup = TG_PostUpdateWork;

	static ConstructorHelpers::FObjectFinder<UStaticMesh> CubeMeshFinder(TEXT("/Engine/BasicShapes/Cube"));
	if (CubeMeshFinder.Succeeded())
	{
		CubeMeshAsset = CubeMeshFinder.Object;
	}

	static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereMeshFinder(TEXT("/Engine/BasicShapes/Sphere"));
	if (SphereMeshFinder.Succeeded())
	{
		SphereMeshAsset = SphereMeshFinder.Object;
	}
}

void UMoALiveLinkPropComponent::BeginPlay()
{
	Super::BeginPlay();

	IModularFeatures& ModularFeatures = IModularFeatures::Get();
	if (ModularFeatures.IsModularFeatureAvailable(ILiveLinkClient::ModularFeatureName))
	{
		LiveLinkClient = &IModularFeatures::Get().GetModularFeature<ILiveLinkClient>(ILiveLinkClient::ModularFeatureName);
	}
	else
	{
		UE_LOG(LogTemp, Error, TEXT("[MoA Prop Component] LiveLink Client not available!"));
	}
}

void UMoALiveLinkPropComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	Super::EndPlay(EndPlayReason);

	// Cleanup visualization actor
	if (VisualizationActor)
	{
		VisualizationActor->Destroy();
		VisualizationActor = nullptr;
	}
}

#if WITH_EDITOR
void UMoALiveLinkPropComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
	Super::PostEditChangeProperty(PropertyChangedEvent);

	if (PropertyChangedEvent.Property != nullptr)
	{
		FName PropertyName = PropertyChangedEvent.Property->GetFName();
		
		if (PropertyName == GET_MEMBER_NAME_CHECKED(UMoALiveLinkPropComponent, bEnableVisualization))
		{
			if (bEnableVisualization)
			{
				if (!VisualizationActor && PropSize.Size() > 0.1f)
				{
					UpdateVisualizationMesh();
				}
			}
			else
			{
				if (VisualizationActor)
				{
					VisualizationActor->Destroy();
					VisualizationActor = nullptr;
				}
			}
		}
	}
}
#endif

void UMoALiveLinkPropComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	if (!LiveLinkClient || SubjectName.IsNone())
	{
		return;
	}

	FLiveLinkSubjectFrameData FrameData;
	if (LiveLinkClient->EvaluateFrame_AnyThread(SubjectName, ULiveLinkTransformRole::StaticClass(), FrameData))
	{
		FLiveLinkTransformFrameData* TransformData = FrameData.FrameData.Cast<FLiveLinkTransformFrameData>();
		if (TransformData)
		{
			LastDataReceivedTime = FPlatformTime::Seconds();

			if (TransformData->PropertyValues.Num() > 0)
			{
				int32 NewPropType = static_cast<int32>(TransformData->PropertyValues[0]);
				
				if (NewPropType != LastPropType)
				{
					LastPropType = NewPropType;
					if (bEnableVisualization)
					{
						UpdateVisualizationMesh();
					}
				}
			}

			FTransform ReceivedTransform = TransformData->Transform;
			FVector Position = ReceivedTransform.GetLocation();
			FQuat Rotation = ReceivedTransform.GetRotation();
			FVector Scale = ReceivedTransform.GetScale3D();

			FVector NewPropSize;
			if (bConvertCoordinateSystem)
			{
				NewPropSize = FVector(Scale.X, Scale.Z, Scale.Y) * 100.0f;
			}
			else
			{
				NewPropSize = Scale * 100.0f;
			}
			
			bool bSizeChanged = !PropSize.Equals(NewPropSize, 0.1f);
			PropSize = NewPropSize;

			if (bConvertCoordinateSystem)
			{
				Position = ConvertPosition(Position);
				Rotation = ConvertRotation(Rotation);
			}

			// Apply manual position scale
			if (bUsePositionScale)
			{
				Position *= ManualPositionScale;
			}

			AActor* Owner = GetOwner();
			if (Owner)
			{
				FTransform NewTransform = Owner->GetActorTransform();

				if (bApplyPosition)
				{
					NewTransform.SetLocation(Position);
				}

				if (bApplyRotation)
				{
					FRotator NewRotation = Rotation.Rotator();
					NewTransform.SetRotation(NewRotation.Quaternion());
				}

			Owner->SetActorTransform(NewTransform);
		}
		
		if (bEnableVisualization)
		{

			if (!VisualizationActor || bSizeChanged)
			{
				UpdateVisualizationMesh();
			}
		}
		else
		{
			if (VisualizationActor)
			{
				VisualizationActor->Destroy();
				VisualizationActor = nullptr;
			}
		}
	}
}
}

bool UMoALiveLinkPropComponent::IsReceivingData() const
{
	if (LastDataReceivedTime == 0.0)
	{
		return false;
	}

	double CurrentTime = FPlatformTime::Seconds();
	return (CurrentTime - LastDataReceivedTime) < 1.0;
}

void UMoALiveLinkPropComponent::SetSubjectByNameAndNum(const FString& PropName, int32 PropNum)
{
	FString SubjectNameStr = FString::Printf(TEXT("%s_%d"), *PropName, PropNum);
	SubjectName = FName(*SubjectNameStr);
}

TArray<FString> UMoALiveLinkPropComponent::GetAvailableSubjectNames() const
{
	TArray<FString> SubjectNames;
	
	IModularFeatures& ModularFeatures = IModularFeatures::Get();
	if (ModularFeatures.IsModularFeatureAvailable(ILiveLinkClient::ModularFeatureName))
	{
		ILiveLinkClient* Client = &ModularFeatures.GetModularFeature<ILiveLinkClient>(ILiveLinkClient::ModularFeatureName);
		
		TArray<FLiveLinkSubjectKey> SubjectKeys = Client->GetSubjects(true, true);
		
		for (const FLiveLinkSubjectKey& Key : SubjectKeys)
		{
			TSubclassOf<ULiveLinkRole> SubjectRole = Client->GetSubjectRole_AnyThread(Key);
			if (SubjectRole == ULiveLinkTransformRole::StaticClass())
			{
				SubjectNames.Add(Key.SubjectName.ToString());
			}
		}
	}
	
	if (SubjectNames.Num() == 0)
	{
		SubjectNames.Add(TEXT("(No Transform Subjects Available)"));
	}
	
	return SubjectNames;
}

void UMoALiveLinkPropComponent::RefreshSubjectList()
{
	GetAvailableSubjectNames();
}

FVector UMoALiveLinkPropComponent::ConvertPosition(const FVector& SourcePosition) const
{
	return 100.0f * FVector(-SourcePosition.X, SourcePosition.Z, SourcePosition.Y);
}

FQuat UMoALiveLinkPropComponent::ConvertRotation(const FQuat& SourceRotation) const
{
	return FQuat(-SourceRotation.X, SourceRotation.Z, SourceRotation.Y, SourceRotation.W);
}

void UMoALiveLinkPropComponent::CalibrateVisualization(int32 PropType, float Width, float Height, float Depth)
{
	PropSize = FVector(Width, Height, Depth);
	LastPropType = PropType;

	UE_LOG(LogTemp, Log, TEXT("[MoA Prop] CalibrateVisualization - Type:%d, Size:(%.2f, %.2f, %.2f)"),
		PropType, Width, Height, Depth);

	if (bEnableVisualization)
	{
		UpdateVisualizationMesh();
	}
}

void UMoALiveLinkPropComponent::UpdateVisualizationMesh()
{
	if (!bEnableVisualization)
	{
		if (VisualizationActor)
		{
			VisualizationActor->Destroy();
			VisualizationActor = nullptr;
		}
		return;
	}

	switch (LastPropType)
	{
		case 0:
			CreateCubeMesh();
			break;
		case 1:
			CreateSphereMesh();
			break;
		default:
			CreateCubeMesh();
			break;
	}
}

void UMoALiveLinkPropComponent::CreateCubeMesh()
{
	AActor* Owner = GetOwner();
	if (!Owner || !Owner->GetWorld() || !CubeMeshAsset)
	{
		return;
	}

	// Destroy existing visualization actor
	if (VisualizationActor)
	{
		VisualizationActor->Destroy();
		VisualizationActor = nullptr;
	}

	// Spawn new actor
	FActorSpawnParameters SpawnParams;
	SpawnParams.Owner = Owner;
	SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
	
	VisualizationActor = Owner->GetWorld()->SpawnActor<AActor>(
		AActor::StaticClass(),
		FVector::ZeroVector,
		FRotator::ZeroRotator,
		SpawnParams);

	if (VisualizationActor)
	{
		VisualizationActor->SetActorLabel(TEXT("PropVisualization_Cube"));

		// Create static mesh component
		UStaticMeshComponent* MeshComp = NewObject<UStaticMeshComponent>(
			VisualizationActor,
			UStaticMeshComponent::StaticClass(),
			TEXT("CubeMesh"));
		
		if (MeshComp)
		{
			MeshComp->SetStaticMesh(CubeMeshAsset);
			MeshComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
			MeshComp->SetVisibility(true);
			MeshComp->SetHiddenInGame(false);
			MeshComp->RegisterComponent();
			
			VisualizationActor->SetRootComponent(MeshComp);

			// Attach to owner
			VisualizationActor->AttachToActor(Owner, FAttachmentTransformRules::KeepRelativeTransform);

			// Set transform
			FVector MeshScale = PropSize / 100.0f;
			FVector MeshPosition = FVector(
				-((PropSize.X / 2.0f) - 3.75f),
				(PropSize.Y / 2.0f) - 3.0f,
				-(PropSize.Z / 2.0f) - 2.0f
			);

			VisualizationActor->SetActorRelativeScale3D(MeshScale);
			VisualizationActor->SetActorRelativeLocation(MeshPosition);

			UE_LOG(LogTemp, Log, TEXT("[MoA Prop] Cube Actor Created - PropSize: (%.2f, %.2f, %.2f), Scale: (%.4f, %.4f, %.4f)"),
				PropSize.X, PropSize.Y, PropSize.Z, MeshScale.X, MeshScale.Y, MeshScale.Z);
		}
	}
}

void UMoALiveLinkPropComponent::CreateSphereMesh()
{
	AActor* Owner = GetOwner();
	if (!Owner || !Owner->GetWorld() || !SphereMeshAsset)
	{
		return;
	}

	// Destroy existing visualization actor
	if (VisualizationActor)
	{
		VisualizationActor->Destroy();
		VisualizationActor = nullptr;
	}

	// Spawn new actor
	FActorSpawnParameters SpawnParams;
	SpawnParams.Owner = Owner;
	SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
	
	VisualizationActor = Owner->GetWorld()->SpawnActor<AActor>(
		AActor::StaticClass(),
		FVector::ZeroVector,
		FRotator::ZeroRotator,
		SpawnParams);

	if (VisualizationActor)
	{
		VisualizationActor->SetActorLabel(TEXT("PropVisualization_Sphere"));

		// Create static mesh component
		UStaticMeshComponent* MeshComp = NewObject<UStaticMeshComponent>(
			VisualizationActor,
			UStaticMeshComponent::StaticClass(),
			TEXT("SphereMesh"));
		
		if (MeshComp)
		{
			MeshComp->SetStaticMesh(SphereMeshAsset);
			MeshComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
			MeshComp->SetVisibility(true);
			MeshComp->SetHiddenInGame(false);
			MeshComp->RegisterComponent();
			
			VisualizationActor->SetRootComponent(MeshComp);

			// Attach to owner
			VisualizationActor->AttachToActor(Owner, FAttachmentTransformRules::KeepRelativeTransform);

			// Set transform
			float Diameter = PropSize.Y / 100.0f;
			FVector MeshScale = FVector(Diameter, Diameter, Diameter);
			FVector MeshPosition = FVector(
				0.0f,
				0.0f,
				-(PropSize.Y / 2.0f) - 2.0f
			);

			VisualizationActor->SetActorRelativeScale3D(MeshScale);
			VisualizationActor->SetActorRelativeLocation(MeshPosition);

			UE_LOG(LogTemp, Log, TEXT("[MoA Prop] Sphere Actor Created - PropSize: (%.2f, %.2f, %.2f), Diameter: %.4f, Scale: (%.4f, %.4f, %.4f)"),
				PropSize.X, PropSize.Y, PropSize.Z, Diameter, MeshScale.X, MeshScale.Y, MeshScale.Z);
		}
	}
}

