// Copyright Pacificao. All Rights Reserved.

#include "AgrarianShelterActor.h"
#include "AgrarianPersistentActorComponent.h"
#include "Components/BoxComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Engine/StaticMesh.h"
#include "Materials/MaterialInterface.h"
#include "Net/UnrealNetwork.h"
#include "UObject/ConstructorHelpers.h"

namespace
{
	void ConfigureShelterProxyComponent(UStaticMeshComponent* Component, UStaticMesh* MeshAsset, UMaterialInterface* MaterialAsset)
	{
		if (!Component)
		{
			return;
		}

		Component->SetCollisionEnabled(ECollisionEnabled::NoCollision);
		Component->SetGenerateOverlapEvents(false);
		if (MeshAsset)
		{
			Component->SetStaticMesh(MeshAsset);
		}
		if (MaterialAsset)
		{
			Component->SetMaterial(0, MaterialAsset);
		}
	}
}

AAgrarianShelterActor::AAgrarianShelterActor()
{
	bReplicates = true;
	SetNetCullDistanceSquared(FMath::Square(6000.0f));

	Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
	RootComponent = Mesh;
	Mesh->SetCollisionProfileName(TEXT("BlockAll"));

	static ConstructorHelpers::FObjectFinder<UStaticMesh> CubeMesh(TEXT("/Game/Agrarian/Environment/PlaceholderMeshes/SM_AGR_Placeholder_Cube.SM_AGR_Placeholder_Cube"));
	static ConstructorHelpers::FObjectFinder<UStaticMesh> ChamferCubeMesh(TEXT("/Game/Agrarian/Environment/PlaceholderMeshes/SM_AGR_Placeholder_ChamferCube.SM_AGR_Placeholder_ChamferCube"));
	static ConstructorHelpers::FObjectFinder<UStaticMesh> CylinderMesh(TEXT("/Game/Agrarian/Environment/PlaceholderMeshes/SM_AGR_Placeholder_Cylinder.SM_AGR_Placeholder_Cylinder"));
	static ConstructorHelpers::FObjectFinder<UMaterialInterface> WoodMaterial(TEXT("/Game/Agrarian/Materials/M_AGR_GZ_Wood_Resource.M_AGR_GZ_Wood_Resource"));
	static ConstructorHelpers::FObjectFinder<UMaterialInterface> FiberMaterial(TEXT("/Game/Agrarian/Materials/M_AGR_GZ_Fiber_Resource.M_AGR_GZ_Fiber_Resource"));

	if (CubeMesh.Succeeded())
	{
		Mesh->SetStaticMesh(CubeMesh.Object);
		Mesh->SetRelativeScale3D(FVector(3.2f, 2.6f, 0.08f));
	}
	if (WoodMaterial.Succeeded())
	{
		Mesh->SetMaterial(0, WoodMaterial.Object);
	}

	FloorProxy = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("FloorProxy"));
	FloorProxy->SetupAttachment(RootComponent);
	ConfigureShelterProxyComponent(FloorProxy, CubeMesh.Succeeded() ? CubeMesh.Object : nullptr, WoodMaterial.Succeeded() ? WoodMaterial.Object : nullptr);
	FloorProxy->SetRelativeLocation(FVector(0.0f, 0.0f, 10.0f));
	FloorProxy->SetRelativeScale3D(FVector(3.0f, 2.4f, 0.08f));

	BackWallProxy = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("BackWallProxy"));
	BackWallProxy->SetupAttachment(RootComponent);
	ConfigureShelterProxyComponent(BackWallProxy, CubeMesh.Succeeded() ? CubeMesh.Object : nullptr, FiberMaterial.Succeeded() ? FiberMaterial.Object : nullptr);
	BackWallProxy->SetRelativeLocation(FVector(-140.0f, 0.0f, 105.0f));
	BackWallProxy->SetRelativeScale3D(FVector(0.14f, 2.2f, 1.5f));

	LeftRoofProxy = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("LeftRoofProxy"));
	LeftRoofProxy->SetupAttachment(RootComponent);
	ConfigureShelterProxyComponent(LeftRoofProxy, ChamferCubeMesh.Succeeded() ? ChamferCubeMesh.Object : (CubeMesh.Succeeded() ? CubeMesh.Object : nullptr), FiberMaterial.Succeeded() ? FiberMaterial.Object : nullptr);
	LeftRoofProxy->SetRelativeLocation(FVector(8.0f, -82.0f, 168.0f));
	LeftRoofProxy->SetRelativeRotation(FRotator(0.0f, 0.0f, -24.0f));
	LeftRoofProxy->SetRelativeScale3D(FVector(3.1f, 0.16f, 1.45f));

	RightRoofProxy = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("RightRoofProxy"));
	RightRoofProxy->SetupAttachment(RootComponent);
	ConfigureShelterProxyComponent(RightRoofProxy, ChamferCubeMesh.Succeeded() ? ChamferCubeMesh.Object : (CubeMesh.Succeeded() ? CubeMesh.Object : nullptr), FiberMaterial.Succeeded() ? FiberMaterial.Object : nullptr);
	RightRoofProxy->SetRelativeLocation(FVector(8.0f, 82.0f, 168.0f));
	RightRoofProxy->SetRelativeRotation(FRotator(0.0f, 0.0f, 24.0f));
	RightRoofProxy->SetRelativeScale3D(FVector(3.1f, 0.16f, 1.45f));

	FrameProxyA = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("FrameProxyA"));
	FrameProxyA->SetupAttachment(RootComponent);
	ConfigureShelterProxyComponent(FrameProxyA, CylinderMesh.Succeeded() ? CylinderMesh.Object : nullptr, WoodMaterial.Succeeded() ? WoodMaterial.Object : nullptr);
	FrameProxyA->SetRelativeLocation(FVector(95.0f, -94.0f, 96.0f));
	FrameProxyA->SetRelativeRotation(FRotator(0.0f, 0.0f, -12.0f));
	FrameProxyA->SetRelativeScale3D(FVector(0.12f, 0.12f, 1.92f));

	FrameProxyB = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("FrameProxyB"));
	FrameProxyB->SetupAttachment(RootComponent);
	ConfigureShelterProxyComponent(FrameProxyB, CylinderMesh.Succeeded() ? CylinderMesh.Object : nullptr, WoodMaterial.Succeeded() ? WoodMaterial.Object : nullptr);
	FrameProxyB->SetRelativeLocation(FVector(95.0f, 94.0f, 96.0f));
	FrameProxyB->SetRelativeRotation(FRotator(0.0f, 0.0f, 12.0f));
	FrameProxyB->SetRelativeScale3D(FVector(0.12f, 0.12f, 1.92f));

	ProtectionVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("ProtectionVolume"));
	ProtectionVolume->SetupAttachment(RootComponent);
	ProtectionVolume->SetBoxExtent(FVector(250.0f, 250.0f, 180.0f));
	ProtectionVolume->SetCollisionProfileName(TEXT("OverlapAllDynamic"));

	PersistentActorComponent = CreateDefaultSubobject<UAgrarianPersistentActorComponent>(TEXT("PersistentActorComponent"));
	PersistentActorComponent->ActorTypeId = TEXT("primitive_shelter");
}

void AAgrarianShelterActor::BeginPlay()
{
	Super::BeginPlay();
	ClampStructureHealth();
}

float AAgrarianShelterActor::TakeDamage(float DamageAmount, const FDamageEvent& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
	const float PreviousHealth = CurrentStructureHealth;
	ApplyStructureDamage(DamageAmount, DamageCauser);
	return FMath::Max(0.0f, PreviousHealth - CurrentStructureHealth);
}

void AAgrarianShelterActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(AAgrarianShelterActor, MaxStructureHealth);
	DOREPLIFETIME(AAgrarianShelterActor, CurrentStructureHealth);
}

void AAgrarianShelterActor::CapturePersistentState_Implementation(UAgrarianPersistentActorComponent* PersistentComponent) const
{
	if (!PersistentComponent)
	{
		return;
	}

	PersistentComponent->NumberState.Add(TEXT("max_structure_health"), MaxStructureHealth);
	PersistentComponent->NumberState.Add(TEXT("current_structure_health"), CurrentStructureHealth);
}

void AAgrarianShelterActor::ApplyPersistentState_Implementation(UAgrarianPersistentActorComponent* PersistentComponent)
{
	if (!HasAuthority() || !PersistentComponent)
	{
		return;
	}

	if (const float* SavedMaxHealth = PersistentComponent->NumberState.Find(TEXT("max_structure_health")))
	{
		MaxStructureHealth = FMath::Max(1.0f, *SavedMaxHealth);
	}

	if (const float* SavedCurrentHealth = PersistentComponent->NumberState.Find(TEXT("current_structure_health")))
	{
		CurrentStructureHealth = *SavedCurrentHealth;
	}

	ClampStructureHealth();
}

bool AAgrarianShelterActor::ApplyStructureDamage(float DamageAmount, AActor* DamageCauser)
{
	if (!HasAuthority() || DamageAmount <= 0.0f || CurrentStructureHealth <= 0.0f)
	{
		return false;
	}

	CurrentStructureHealth = FMath::Clamp(CurrentStructureHealth - DamageAmount, 0.0f, MaxStructureHealth);
	if (CurrentStructureHealth <= 0.0f && bDestroyWhenHealthDepleted)
	{
		Destroy();
	}

	return true;
}

bool AAgrarianShelterActor::RepairStructure(float RepairAmount)
{
	if (!HasAuthority() || RepairAmount <= 0.0f || CurrentStructureHealth >= MaxStructureHealth)
	{
		return false;
	}

	CurrentStructureHealth = FMath::Clamp(CurrentStructureHealth + RepairAmount, 0.0f, MaxStructureHealth);
	return true;
}

bool AAgrarianShelterActor::Deconstruct(AActor* RequestingActor)
{
	if (!HasAuthority() || !bCanBeDeconstructed)
	{
		return false;
	}

	Destroy();
	return true;
}

float AAgrarianShelterActor::GetStructureHealthRatio() const
{
	return MaxStructureHealth > 0.0f ? FMath::Clamp(CurrentStructureHealth / MaxStructureHealth, 0.0f, 1.0f) : 0.0f;
}

bool AAgrarianShelterActor::IsStructureDamaged() const
{
	return CurrentStructureHealth < MaxStructureHealth;
}

void AAgrarianShelterActor::OnRep_StructureHealth()
{
	ClampStructureHealth();
}

void AAgrarianShelterActor::ClampStructureHealth()
{
	MaxStructureHealth = FMath::Max(1.0f, MaxStructureHealth);
	CurrentStructureHealth = FMath::Clamp(CurrentStructureHealth, 0.0f, MaxStructureHealth);
}
