// Copyright Pacificao. All Rights Reserved.

#include "AgrarianCampfire.h"
#include "AgrarianGameCharacter.h"
#include "AgrarianGameState.h"
#include "AgrarianInventoryComponent.h"
#include "AgrarianPersistentActorComponent.h"
#include "AgrarianSurvivalComponent.h"
#include "Particles/ParticleSystemComponent.h"
#include "Components/PointLightComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Kismet/GameplayStatics.h"
#include "Net/UnrealNetwork.h"

AAgrarianCampfire::AAgrarianCampfire()
{
	PrimaryActorTick.bCanEverTick = true;
	bReplicates = true;
	NetCullDistanceSquared = FMath::Square(6000.0f);

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

	FireLight = CreateDefaultSubobject<UPointLightComponent>(TEXT("FireLight"));
	FireLight->SetupAttachment(RootComponent);
	FireLight->SetIntensity(0.0f);
	FireLight->SetAttenuationRadius(WarmthRadius);
	FireLight->SetLightColor(FLinearColor(1.0f, 0.45f, 0.18f));

	SmokeEffect = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("SmokeEffect"));
	SmokeEffect->SetupAttachment(RootComponent);
	SmokeEffect->bAutoActivate = false;
	SmokeEffect->SetRelativeLocation(FVector(0.0f, 0.0f, 80.0f));
	SmokeEffect->SetVisibility(false);

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

void AAgrarianCampfire::Tick(float DeltaSeconds)
{
	Super::Tick(DeltaSeconds);

	if (HasAuthority() && bLit)
	{
		FuelSeconds = FMath::Max(0.0f, FuelSeconds - (DeltaSeconds * GetWeatherFuelDrainMultiplier()));
		if (FuelSeconds <= 0.0f || (bWetWeatherCanExtinguish && IsWetWeatherActive() && FuelSeconds <= WetWeatherExtinguishFuelThresholdSeconds))
		{
			Extinguish();
		}

		if (CanCook())
		{
			CookingProgressSeconds = FMath::Min(CookingSecondsRequired, CookingProgressSeconds + DeltaSeconds);
		}

		WarmNearbyCharacters(DeltaSeconds);
	}
}

void AAgrarianCampfire::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(AAgrarianCampfire, bLit);
	DOREPLIFETIME(AAgrarianCampfire, FuelSeconds);
	DOREPLIFETIME(AAgrarianCampfire, bCookingPlaceholderEnabled);
	DOREPLIFETIME(AAgrarianCampfire, CookingSecondsRequired);
	DOREPLIFETIME(AAgrarianCampfire, CookingProgressSeconds);
}

FText AAgrarianCampfire::GetInteractionText_Implementation(const AAgrarianGameCharacter* Interactor) const
{
	return bLit ? FText::FromString(TEXT("Add fuel")) : FText::FromString(TEXT("Light fire"));
}

bool AAgrarianCampfire::CanInteract_Implementation(const AAgrarianGameCharacter* Interactor) const
{
	return Interactor != nullptr;
}

void AAgrarianCampfire::Interact_Implementation(AAgrarianGameCharacter* Interactor)
{
	if (!HasAuthority() || !Interactor)
	{
		return;
	}

	UAgrarianInventoryComponent* Inventory = Interactor->GetInventoryComponent();
	if (Inventory && Inventory->RemoveItem(TEXT("wood"), 1))
	{
		AddFuel(90.0f);
	}
}

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

	PersistentComponent->NumberState.Add(TEXT("lit"), bLit ? 1.0f : 0.0f);
	PersistentComponent->NumberState.Add(TEXT("fuel_seconds"), FuelSeconds);
	PersistentComponent->NumberState.Add(TEXT("cooking_placeholder_enabled"), bCookingPlaceholderEnabled ? 1.0f : 0.0f);
	PersistentComponent->NumberState.Add(TEXT("cooking_seconds_required"), CookingSecondsRequired);
	PersistentComponent->NumberState.Add(TEXT("cooking_progress_seconds"), CookingProgressSeconds);
}

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

	const float* SavedFuelSeconds = PersistentComponent->NumberState.Find(TEXT("fuel_seconds"));
	const float* SavedCookingEnabled = PersistentComponent->NumberState.Find(TEXT("cooking_placeholder_enabled"));
	const float* SavedCookingRequired = PersistentComponent->NumberState.Find(TEXT("cooking_seconds_required"));
	const float* SavedCookingProgress = PersistentComponent->NumberState.Find(TEXT("cooking_progress_seconds"));
	const float* SavedLit = PersistentComponent->NumberState.Find(TEXT("lit"));

	if (SavedFuelSeconds)
	{
		FuelSeconds = FMath::Max(0.0f, *SavedFuelSeconds);
	}

	if (SavedCookingEnabled)
	{
		bCookingPlaceholderEnabled = *SavedCookingEnabled > 0.5f;
	}

	if (SavedCookingRequired)
	{
		CookingSecondsRequired = FMath::Max(0.0f, *SavedCookingRequired);
	}

	if (SavedCookingProgress)
	{
		CookingProgressSeconds = FMath::Clamp(*SavedCookingProgress, 0.0f, CookingSecondsRequired);
	}

	SetLit(SavedLit && *SavedLit > 0.5f && FuelSeconds > 0.0f);
}

void AAgrarianCampfire::AddFuel(float Seconds)
{
	if (HasAuthority())
	{
		FuelSeconds += FMath::Max(0.0f, Seconds);
		if (FuelSeconds > 0.0f)
		{
			SetLit(true);
		}
		else
		{
			UpdateVisualState();
		}
	}
}

void AAgrarianCampfire::Extinguish()
{
	if (HasAuthority())
	{
		FuelSeconds = 0.0f;
		SetLit(false);
	}
}

bool AAgrarianCampfire::CanCook() const
{
	return bLit && bCookingPlaceholderEnabled && CookingSecondsRequired > 0.0f;
}

float AAgrarianCampfire::GetCookingProgressRatio() const
{
	if (CookingSecondsRequired <= 0.0f)
	{
		return 0.0f;
	}

	return FMath::Clamp(CookingProgressSeconds / CookingSecondsRequired, 0.0f, 1.0f);
}

float AAgrarianCampfire::GetWeatherFuelDrainMultiplier() const
{
	switch (GetCurrentWeather())
	{
	case EAgrarianWeatherType::Rain:
		return FMath::Max(1.0f, RainFuelDrainMultiplier);
	case EAgrarianWeatherType::Storm:
		return FMath::Max(1.0f, StormFuelDrainMultiplier);
	default:
		return 1.0f;
	}
}

bool AAgrarianCampfire::IsWetWeatherActive() const
{
	const EAgrarianWeatherType CurrentWeather = GetCurrentWeather();
	return CurrentWeather == EAgrarianWeatherType::Rain || CurrentWeather == EAgrarianWeatherType::Storm;
}

void AAgrarianCampfire::OnRep_FireState()
{
	UpdateVisualState();
}

EAgrarianWeatherType AAgrarianCampfire::GetCurrentWeather() const
{
	if (const UWorld* World = GetWorld())
	{
		if (const AAgrarianGameState* GameState = World->GetGameState<AAgrarianGameState>())
		{
			return GameState->Weather;
		}
	}

	return EAgrarianWeatherType::Clear;
}

void AAgrarianCampfire::SetLit(bool bNewLit)
{
	if (bLit != bNewLit)
	{
		bLit = bNewLit;
	}

	UpdateVisualState();
}

void AAgrarianCampfire::UpdateVisualState()
{
	if (FireLight)
	{
		FireLight->SetIntensity(bLit ? 4200.0f : 0.0f);
	}

	if (SmokeEffect)
	{
		SmokeEffect->SetVisibility(bLit);
		if (bLit)
		{
			SmokeEffect->ActivateSystem();
		}
		else
		{
			SmokeEffect->DeactivateSystem();
		}
	}
}

void AAgrarianCampfire::WarmNearbyCharacters(float DeltaSeconds)
{
	TArray<AActor*> Characters;
	UGameplayStatics::GetAllActorsOfClass(this, AAgrarianGameCharacter::StaticClass(), Characters);

	for (AActor* Actor : Characters)
	{
		AAgrarianGameCharacter* Character = Cast<AAgrarianGameCharacter>(Actor);
		if (!Character || FVector::DistSquared(Character->GetActorLocation(), GetActorLocation()) > FMath::Square(WarmthRadius))
		{
			continue;
		}

		if (UAgrarianSurvivalComponent* SurvivalComponent = Character->GetSurvivalComponent())
		{
			SurvivalComponent->AddWarmth(WarmthPerSecond * DeltaSeconds);
		}
	}
}
