// Copyright (c) Microsoft Corporation. 
// Licensed under the MIT license.

#include "ClippingPlaneDataVisualizer.h"
#include "CoreMinimal.h"
#include "LevelEditor.h"
#include "Editor.h"
#include "UObject/ConstructorHelpers.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Algo/AnyOf.h"
#include "UObject/UObjectBaseUtility.h"
#include "UObject/ObjectMacros.h"
#include "ISimplygonReduction.h"
#include "SimplygonPluginMeshReductionModule.h"
#include "SimplygonEditorModule.h"
#include "DrawDebugHelpers.h"

ASimplygonClippingPlanesVisualizer::ASimplygonClippingPlanesVisualizer(const FObjectInitializer& InInitializer)
	: AActor(InInitializer)
{
#if WITH_EDITORONLY_DATA
	PlaneVisualizationScale = 100.0f;
	Component = CreateDefaultSubobject<UInstancedStaticMeshComponent>(TEXT("InstancedStaticMeshComponent0"));
	RootComponent = Component;
	Component->Mobility = EComponentMobility::Static;
	Component->SetGenerateOverlapEvents(false);
	Component->bSelectable = false;
	Component->SetCollisionEnabled(ECollisionEnabled::NoCollision);
	Component->CastShadow = false;
	Component->bCastStaticShadow = false;
	Component->bCastDynamicShadow = false;
	Component->bAllowCullDistanceVolume = false;

	SpriteComponent = CreateEditorOnlyDefaultSubobject<UBillboardComponent>(TEXT("Sprite"));
	if (!IsRunningCommandlet() && (SpriteComponent != nullptr))
	{
		// Structure to hold one-time initialization
		struct FConstructorStatics
		{
			ConstructorHelpers::FObjectFinderOptional<UTexture2D> SpriteTexture;
			FName ID_Info;
			FText NAME_Info;
			FConstructorStatics()
				: SpriteTexture(TEXT("/Simplygon/Textures/SimplygonClippingPlanes"))
				, ID_Info(TEXT("SimplygonCuttingPlaneVisualizer"))
				, NAME_Info(NSLOCTEXT("SpriteCategory", "SimplygonCuttingPlaneVisualizer", "SimplygonCuttingPlaneVisualizer"))
			{
			}
		};
		static FConstructorStatics ConstructorStatics;

		SpriteComponent->Sprite = ConstructorStatics.SpriteTexture.Get();
		SpriteComponent->SetRelativeScale3D(FVector(0.5f, 0.5f, 0.5f));
		SpriteComponent->SpriteInfo.Category = ConstructorStatics.ID_Info;
		SpriteComponent->SpriteInfo.DisplayName = ConstructorStatics.NAME_Info;
		SpriteComponent->bIsScreenSizeScaled = true;
		SpriteComponent->bHiddenInGame = true;
		SpriteComponent->SetupAttachment(Component);
		SpriteComponent->bIsScreenSizeScaled = true;

	}

	//bLockLocation = true;
	bIsEditorOnlyActor = true;
	SetActorEnableCollision(false);
	ConstructorHelpers::FObjectFinder<UStaticMesh> MeshSphereAsset(TEXT("/Engine/BasicShapes/Plane.Plane"));
	static ConstructorHelpers::FObjectFinder<UMaterial> MaterialAsset(TEXT("/Simplygon/Materials/M_ClippingPlaneVisualizer.M_ClippingPlaneVisualizer"));
		

	if (MeshSphereAsset.Succeeded())
	{
		Component->SetStaticMesh(MeshSphereAsset.Object);
		Component->bIsEditorOnly = true;
	}

	if (MaterialAsset.Succeeded())
	{
		Component->SetMaterial(0, MaterialAsset.Object);
	}

#endif
}

#if WITH_EDITOR
void ASimplygonClippingPlanesVisualizer::PostEditMove(bool bFinished)
{
	UpdateInstances();
	AActor::PostEditMove(bFinished);
}

void ASimplygonClippingPlanesVisualizer::PostEditChangeChainProperty(struct FPropertyChangedChainEvent& e)
{
	
	int32 ItemIndex = e.GetArrayIndex(TEXT("CuttingPlanes"));
	UpdateInstances();
	AActor::PostEditChangeChainProperty(e);
}

void ASimplygonClippingPlanesVisualizer::PostLoad()
{
	UpdateInstances();
	AActor::PostLoad();
}
#endif

void ASimplygonClippingPlanesVisualizer::UpdateMeshRedutionModule()
{
#if WITH_EDITORONLY_DATA
	if (!FModuleManager::Get().IsModuleLoaded(TEXT("SimplygonPluginMeshReduction")))
	{
		return;
	}

	ISimplygonPluginMeshReductionModule& SimplygonModule = FModuleManager::GetModuleChecked<ISimplygonPluginMeshReductionModule>("SimplygonPluginMeshReduction");


	TArray<FCuttingPlane> TransformedPlanes(CuttingPlanes);
	int32 NumInstances = Component->GetInstanceCount();
	for (int32 Index = 0; Index < NumInstances; ++Index)
	{
		FTransform InstanceTransform = FTransform::Identity;
		Component->GetInstanceTransform(Index, InstanceTransform, true);
		TransformedPlanes[Index].Position = InstanceTransform.GetTranslation();
	}

	TArray<FPlane> InPlanes;
	for (auto& Plane : TransformedPlanes)
	{
		InPlanes.Add(Plane.ToFPlane());
	}



	SimplygonModule.GetSimplygonReduction()->GetCuttingPlaneUpdateHandler().ExecuteIfBound(InPlanes);
#endif
}

void ASimplygonClippingPlanesVisualizer::UpdateInstances()
{
#if WITH_EDITORONLY_DATA
	FVector UniformScale(PlaneVisualizationScale, PlaneVisualizationScale, PlaneVisualizationScale);
	
	Component->ClearInstances();

	for (auto& Plane : CuttingPlanes)
	{
		Component->AddInstanceWorldSpace(FTransform(Plane.GetPlaneRotation(), Plane.Position, UniformScale));
	}
#endif
	UpdateMeshRedutionModule();
}

void ASimplygonClippingPlanesVisualizer::ToggleInEditor()
{
#if WITH_EDITORONLY_DATA
	if(Component->GetVisibleFlag() == false)
		UpdateInstances();

	Component->ToggleVisibility(true);
#endif
}

#if WITH_EDITORONLY_DATA
/** Returns SpriteComponent subobject **/
UBillboardComponent* ASimplygonClippingPlanesVisualizer::GetSpriteComponent() const { return SpriteComponent; }
#endif
