// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Data/GameplayStatEffect.h"
#include "Data/StatForgeDefs.h"
#include "StatForgeComponent.generated.h"

struct FGameplayStatEffectMod;

USTRUCT()
struct FActiveGameplayStatEffect
{
	GENERATED_BODY()
	
	UPROPERTY()
	FGuid Handle;
	
	UPROPERTY()
	TSubclassOf<UGameplayStatEffect> GameplayEffectClass;
	
	UPROPERTY()
	TObjectPtr<UGameplayStatEffect> GameplayEffectInstance;
	
	UPROPERTY()
	TObjectPtr<UObject> SourceObject = nullptr;
	
	UPROPERTY()
	int32 StackCount = 1;
	
	UPROPERTY()
	float RemainingDuration = -1.f;
	
	UPROPERTY()
	float NextTickDuration = -1.f;
	
	UPROPERTY()
	bool bIsTicking = false;
	
	static FActiveGameplayStatEffect CreateNew(UObject* SourceObject, UGameplayStatEffect* Effect, int32 Stacks = 1);
};

UENUM()
enum class EIsStatResult : uint8
{
	Yes,
	No
};

DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnStatChanged, FName, StatName, float, OldValue, float, NewValue);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnGameplayTagsChangedDelegate);

UCLASS(Blueprintable, BlueprintType, ClassGroup=(StatForge), meta=(BlueprintSpawnableComponent))
class STATFORGE_API UStatForgeComponent : public UActorComponent
{
	GENERATED_BODY()

public:
	UStatForgeComponent();
	virtual void BeginPlay() override;
	virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
	
	UFUNCTION(BlueprintCallable, Category = "Stat Forge", meta = (ExpandEnumAsExecs = "Result"))
	static void IsStat(FName StatName, UPARAM(meta = (GetOptions = "StatForgeDeveloperSettings.GetStatNames")) FName CheckedName,  EIsStatResult& Result);

	UFUNCTION(BlueprintPure, Category = "Stat Forge")
	float GetStatValue(UPARAM(meta = (GetOptions = "StatForgeDeveloperSettings.GetStatNames")) const FName StatName) const;
	
	UFUNCTION(BlueprintCallable, Category = "Stat Forge", meta = (AutoCreateRefTerm = "DataValueMap"))
	FGuid ApplyGameplayStatEffect(const TSubclassOf<UGameplayStatEffect> EffectClass, TMap<FName, float> DataValueMap, int32 Stacks = 1);
	
	UFUNCTION(BlueprintCallable, Category = "Stat Forge")
	bool RemoveGameplayStatEffect(FGuid EffectId);
	
	UFUNCTION(BlueprintPure, Category = "Stat Forge")
	bool HasStat(const FName& StatName) const;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Stat Forge|Stats", meta = (GetOptions = "StatForgeDeveloperSettings.GetStatNames"))
	TMap<FName, float> InitialStatValueArray;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Stat Forge|Stats")
	TArray<TSubclassOf<UGameplayStatEffect>> InitialEffectArray;
	
	UPROPERTY(BlueprintAssignable, Category = "Stat Forge|Events")
	FOnStatChanged OnStatChanged;
	
	/** Begin TAGS */
	
	UPROPERTY(BlueprintAssignable, Category = Buffs)
	FOnGameplayTagsChangedDelegate OnTagsChanged;
	
	UFUNCTION(BlueprintCallable, Category = "Stat Forge|Tags")
	void AddGameplayTag(const FGameplayTag& NewTag);

	UFUNCTION(BlueprintCallable, Category = "Stat Forge|Tags")
	void AddGameplayTags(const FGameplayTagContainer& NewTags);

	UFUNCTION(BlueprintCallable, Category = "Stat Forge|Tags")
	bool RemoveGameplayTag(const FGameplayTag& NewTag);

	UFUNCTION(BlueprintCallable, Category = "Stat Forge|Tags")
	void RemoveGameplayTags(const FGameplayTagContainer& NewTags);

	const FGameplayTagContainer& GetGameplayTags() const { return CharacterGameplayTags; }

protected:

	void NotifyGameplayTagsChanged() const;
	/** End TAGS */

	virtual void PreGameplayEffectExecute(UGameplayStatEffect* Effect) {}
	virtual void PostGameplayEffectExecute(UGameplayStatEffect* Effect) {}
	virtual void PreStatModify(const FName StatName) {}
	virtual void PostStatModify(const FName StatName) {}
	void Internal_ApplyGameplayStatEffect(UGameplayStatEffect* Effect);
	virtual void ApplyModToStat(const FGameplayStatEffectMod& Mod, const TMap<FName, float> DataValueMap);
	virtual void RecalculateStatCurrentValue(const FName StatName, const float OldValue);
	static TArray<FName> GetAffectedStatsFromEffect(UGameplayStatEffect* Effect);
	FStatRuntimeDef* GetStatRef(const FName StatName);
	void TickEffects(float DeltaTime);
	bool CanApplyEffect(const UGameplayStatEffect* StatEffect) const;
	bool CanTickEffect(const UGameplayStatEffect* StatEffect) const;
	
	UPROPERTY()
	TArray<FStatRuntimeDef> StatArray;
	
#if WITH_EDITORONLY_DATA
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
	TMap<FName, float> StatValuesPreview;
#endif
	
	UPROPERTY()
	TArray<FActiveGameplayStatEffect> ActiveEffectsArray;
	
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Stat Forge|Tags")
	FGameplayTagContainer CharacterGameplayTags;
};
