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

#pragma once
#include "Components/InstancedStaticMeshComponent.h"
#include "Components/BillboardComponent.h"
#include "Components/ArrowComponent.h"
#include "NavigationSystem.h"
#include "GameFramework/Info.h"
#include "ClippingPlaneDataVisualizer.generated.h"

UENUM()
enum class EPlaneAxis
{
	X,
	Y,
	Z,
};

UENUM()
enum class EPlaneHalfspace
{
	Positive,
	Negative,
};

USTRUCT()
struct FCuttingPlane
{
	GENERATED_BODY()
public:

	UPROPERTY(EditAnywhere,Category= CuttingPlane)
	FVector Position;
	UPROPERTY(EditAnywhere, Category = CuttingPlane)
	EPlaneAxis Axis;
	UPROPERTY(EditAnywhere, Category = CuttingPlane)
	EPlaneHalfspace Halfspace;

	FCuttingPlane()
		:Position(0.0,0.0,0.0)
		,Axis(EPlaneAxis::Z)
		,Halfspace(EPlaneHalfspace::Positive)

	{

	}

	FCuttingPlane(const FCuttingPlane& Other)
		:Position(Other.Position)
		, Axis(Other.Axis)
		, Halfspace(Other.Halfspace)

	{

	}

	bool operator==(const FCuttingPlane& Other) const
	{
		return Position == Other.Position
			&& Axis == Other.Axis
			&& Halfspace == Other.Halfspace;
	}

	bool operator!=(const FCuttingPlane& Other) const
	{
		return !(*this == Other);
	}

	FVector GetNormal() const
	{
		FVector Return = FVector::ZeroVector;
		float NormalDir = Halfspace == EPlaneHalfspace::Positive ? 1.0f : -1.0f;
		switch (Axis)
		{
		case EPlaneAxis::X:
			Return.X = NormalDir;
			break;
		case EPlaneAxis::Y:
			Return.Y = NormalDir;
			break;
		case EPlaneAxis::Z:
		default:
			Return.Z = NormalDir;
			break;
		}
		return Return;
	}

	/**
	 * @brief Get the transform to roate the place mesh to match the plane axis
	 * @return 
	*/
	FRotator GetPlaneRotation()
	{
		FRotator Rotator;
		float Dir = Halfspace == EPlaneHalfspace::Positive ? 1.0f : -1.0f;
		float FlipRotate = Halfspace == EPlaneHalfspace::Positive ? 0.0f : 180.0f;
		//Rotator = Halfspace == EPlaneHalfspace::Negative ? FRotator::MakeFromEuler(FVector(180.f,0,0)) : FRotator::MakeFromEuler(FVector::ZeroVector);
		switch (Axis)
		{
		case EPlaneAxis::X:
			Rotator = FRotator::MakeFromEuler(FVector(FlipRotate, -90.0f * Dir, 0.0f));
			break;
		case EPlaneAxis::Y:
			Rotator = FRotator::MakeFromEuler(FVector(90.0f * Dir, FlipRotate, 0.0f));
			break;
		case EPlaneAxis::Z:
		default:
			Rotator = FRotator::MakeFromEuler(FVector(FlipRotate, 0.0f, 0.0f));
		}
		return Rotator;
	}
	
	FPlane ToFPlane()
	{
		FPlane Plane(0.0,0.0,0.0,0.0);
		float NormalDir = Halfspace == EPlaneHalfspace::Positive ? 1.0f : -1.0f;
		switch (Axis)
		{
		case EPlaneAxis::X:
			Plane.X = NormalDir;
			Plane.W = Position.X;
			break;
		case EPlaneAxis::Y:
			Plane.Y = NormalDir;
			Plane.W = Position.Y;
			break;
		default:
			Plane.Z = NormalDir;
			Plane.W = Position.Z;
			break;
		}

		return Plane;
	}
};

UCLASS(MinimalAPI, hidecategories = (Input, Actor, Rendering, Replication, LOD, Cooking, Collision))
class ASimplygonClippingPlanesVisualizer : public AActor
{
    GENERATED_BODY()


public:
    	
	ASimplygonClippingPlanesVisualizer(const FObjectInitializer& InInitializer);

#if WITH_EDITOR
	virtual void PostEditMove(bool bFinished);
	virtual void PostEditChangeChainProperty(struct FPropertyChangedChainEvent& e);
	virtual void PostLoad();
#endif

	void UpdateInstances();
	void ToggleInEditor();

#if WITH_EDITORONLY_DATA
	/** Returns SpriteComponent subobject **/
	UBillboardComponent* GetSpriteComponent() const;
#endif

private:
	void UpdateMeshRedutionModule();

public:
#if WITH_EDITORONLY_DATA
	UPROPERTY(EditAnywhere, Category=VisualizationSettings)
	float PlaneVisualizationScale;

	UPROPERTY(EditAnywhere, Category=ClippingPlanes)
	TArray<FCuttingPlane> CuttingPlanes;
#endif
private:

#if WITH_EDITORONLY_DATA
	//Each sampling point is a Instanced Static Mesh using a sphere mesh
	UPROPERTY()
	UInstancedStaticMeshComponent* Component;

	UPROPERTY()
	TArray<UArrowComponent*> PlaneDirections;

	/** Billboard Component displayed in editor */
	UPROPERTY()
	UBillboardComponent* SpriteComponent;
#endif
  
};
