#pragma once

#include "CoreMinimal.h"
#include "PalletPulldownBlockable.h"
#include "Interactable.h"
#include "EPalletState.h"
#include "PalletPushSettings.h"
#include "EPalletStunZoneSideStrategy.h"
#include "Engine/HitResult.h"
#include "UObject/NoExportTypes.h"
#include "Pallet.generated.h"

class UMontagePlayer;
class UAnimationMontageSlave;
class USceneComponent;
class UInteractionDefinition;
class UBoxComponent;
class UPrimitiveComponent;
class AActor;
class ADBDPlayer;
class UDBDNavEvadeLoopComponent;
class UPalletPulldownBlockerComponent;
class USpawnedActorReplicationComponent;
class AKiller;
class USkeletalMeshComponent;

UCLASS(Blueprintable)
class DEADBYDAYLIGHT_API APallet : public AInteractable, public IPalletPulldownBlockable
{
	GENERATED_BODY()

public:
	DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPalletStateChangedCosmetic, EPalletState, newState);

public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float PalletDownEvadeDoorFrontPointOffset;

	UPROPERTY(BlueprintReadWrite, BlueprintAssignable)
	FOnPalletStateChangedCosmetic Cosmetic_OnPalletStateChanged;

protected:
	UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Transient)
	EPalletState _state;

	UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Transient)
	bool _isPendingDestruction;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	bool _isIllusionaryPalletDown;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	bool _isDestroyedByEntity;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced)
	UMontagePlayer* _montagePlayer;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced)
	UAnimationMontageSlave* _animationMontageSlave;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced)
	USceneComponent* _leftInteractionSceneComponent;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced)
	USceneComponent* _rightInteractionSceneComponent;

private:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, meta=(AllowPrivateAccess=true))
	UInteractionDefinition* _destroyLeft;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, meta=(AllowPrivateAccess=true))
	UInteractionDefinition* _destroyRight;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, meta=(AllowPrivateAccess=true))
	UBoxComponent* _pushBox;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, meta=(AllowPrivateAccess=true))
	UBoxComponent* _downedPalletCollider;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, meta=(AllowPrivateAccess=true))
	UPrimitiveComponent* _upPalletCollider;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(AllowPrivateAccess=true))
	float _maxDotproductThresholdForPulldownAccross;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(AllowPrivateAccess=true))
	float _fallDuration;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, meta=(AllowPrivateAccess=true))
	UBoxComponent* _stunBoxLeft;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, meta=(AllowPrivateAccess=true))
	UBoxComponent* _stunBoxRight;

	UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Transient, meta=(AllowPrivateAccess=true))
	TSet<AActor*> _stunnedActors;

	UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Transient, meta=(AllowPrivateAccess=true))
	ADBDPlayer* _playerExecutingPulldown;

	UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Transient, meta=(AllowPrivateAccess=true))
	FPalletPushSettings _pushSettings;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, meta=(AllowPrivateAccess=true))
	UDBDNavEvadeLoopComponent* _navEvadeLoopComponent;

	UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Transient, Instanced, meta=(AllowPrivateAccess=true))
	UPalletPulldownBlockerComponent* _palletPulldownBlockerComponent;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(AllowPrivateAccess=true))
	bool _isDreamPallet;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(AllowPrivateAccess=true))
	bool _isFragile;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(AllowPrivateAccess=true))
	bool _canBeDestroyed;

	UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Transient, Instanced, meta=(AllowPrivateAccess=true))
	USpawnedActorReplicationComponent* _spawnedActorReplicationComponent;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(AllowPrivateAccess=true))
	TArray<AKiller*> _stunnedKillers;

public:
	UFUNCTION(BlueprintCallable)
	void SetPendingDestruction(bool isPendingDestruction);

protected:
	UFUNCTION(BlueprintCallable)
	void SetPalletState(EPalletState state);

public:
	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
	void PulldownPallet();

	UFUNCTION(BlueprintCallable)
	void PullDown(ADBDPlayer* player, const FPalletPushSettings& pushSettings, const EPalletStunZoneSideStrategy stunZoneSideStrategy);

private:
	UFUNCTION()
	void OnStunOverlap(UPrimitiveComponent* overlappedComponent, AActor* otherActor, UPrimitiveComponent* otherComp, int32 otherBodyIndex, bool bFromSweep, const FHitResult& sweepResult);

	UFUNCTION()
	void OnPulldownOverlap(UPrimitiveComponent* overlappedComponent, AActor* otherActor, UPrimitiveComponent* otherComp, int32 otherBodyIndex, bool bFromSweep, const FHitResult& sweepResult);

protected:
	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, BlueprintCosmetic)
	void OnPalletStunHit();

	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
	void OnPalletPulledUp_BP();

public:
	UFUNCTION(BlueprintCallable)
	void OnFallen();

private:
	UFUNCTION(BlueprintCallable, NetMulticast, Reliable)
	void Multicast_StunActor(AActor* actor, ADBDPlayer* playerPullingDown, bool stunBreakPallet);

public:
	UFUNCTION(BlueprintCallable, NetMulticast, Reliable)
	void Multicast_PulldownPallet();

	UFUNCTION(BlueprintCallable, NetMulticast, Reliable)
	void Multicast_ExplodePallet(ADBDPlayer* player, bool byEntity);

	UFUNCTION(BlueprintPure)
	bool IsPulldownBlockedByEntity() const;

	UFUNCTION(BlueprintPure)
	bool IsPendingDestruction() const;

	UFUNCTION(BlueprintPure)
	bool IsDestroyedByEntity() const;

	UFUNCTION(BlueprintPure)
	bool IsBeingRepaired() const;

	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
	UPrimitiveComponent* GetZoneForRestrictedPlacement();

protected:
	UFUNCTION(BlueprintPure)
	ADBDPlayer* GetPlayerDoingPulldown() const;

public:
	UFUNCTION(BlueprintPure, BlueprintImplementableEvent)
	USkeletalMeshComponent* GetPalletSkeletalMeshComponent() const;

	UFUNCTION(BlueprintPure)
	bool GetIsPulledDown() const;

	UFUNCTION(BlueprintPure)
	bool GetIsFragile() const;

	UFUNCTION(BlueprintPure)
	bool GetIsDreamPallet() const;

	UFUNCTION(BlueprintPure)
	float GetFallDuration() const;

	UFUNCTION(BlueprintPure, BlueprintNativeEvent)
	void GetDestructibleAreaPrimitiveComponents(TArray<UPrimitiveComponent*>& prims) const;

	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
	void ExplodeStunPallet(ADBDPlayer* player);

	UFUNCTION(BlueprintCallable)
	void ExplodePallet(ADBDPlayer* player, bool kickPallet);

	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
	void EntityExplodePallet(ADBDPlayer* player);

protected:
	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, BlueprintCosmetic)
	void Cosmetic_PalletPullDownFXImmediateHide();

public:
	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, BlueprintCosmetic)
	void Cosmetic_OnPalletPulldownBlockedByEntityShow();

	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, BlueprintCosmetic)
	void Cosmetic_OnPalletPulldownBlockedByEntityHide();

protected:
	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, BlueprintCosmetic)
	void Cosmetic_OnPalletFragileStatusChange(bool isFragilePallet);

public:
	UFUNCTION(BlueprintPure, BlueprintNativeEvent)
	bool CanPulldown(const ADBDPlayer* player, const FVector& interactionAxis, const bool isStationary) const;

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
	bool CanBeDestroyed() const;

protected:
	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
	void BP_ResetPallet();

public:
	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
	void BP_OnFallen();

protected:
	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
	void BP_ExplodePallet(ADBDPlayer* player);

private:
	UFUNCTION()
	void Authority_OnOverlapPushBox(UPrimitiveComponent* overlappedComponent, AActor* otherActor, UPrimitiveComponent* otherComp, int32 otherBodyIndex, bool bFromSweep, const FHitResult& sweepResult);

public:
	APallet();
};

FORCEINLINE uint32 GetTypeHash(const APallet) { return 0; }
