// Project Lab - NHTV Igad

#pragma once

#include "Creatures/NetworkCharacter.h"
#include "IngameSkillTree.h"
#include "PlayerKeyType.h"
#include "NetworkPlayer.generated.h"

/**
 * 
 */
UCLASS()
class UNREALPROJECT_API ANetworkPlayer : public ANetworkCharacter
{
	GENERATED_BODY()
	
public:
	ANetworkPlayer();
	virtual void BeginPlay() override;
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
	virtual void Tick(float DeltaSeconds) override;

	// For detecting when the player is possessed
	virtual void PossessedBy(AController* NewController) override;
	virtual void OnRep_PlayerState() override;

	// Clears all the modifier stat modifications from this character
	virtual void ResetModifiers();

	UFUNCTION(Server, Reliable, WithValidation)
	void ToggleSwitch(class ANetworkSwitch* networkSwitch);

	// Server-Side level up event
	UFUNCTION(BlueprintNativeEvent, Category = "LevelUp")
	void OnLevelUp(int32 newLevel);
	// Called server-side when a character levels up
	void OnLevelUp_Server(int32 newLevel);

	// Get the class properties of this player
	UFUNCTION(BlueprintCallable, Category = "State")
	FCharacterClassProperty GetCharacterClassProperties() const;

	UFUNCTION(BlueprintCallable, Category = "Team")
	class ANetworkPlayer* GetTeamMate() const;

	DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FOnPlayerKilled, FText, source, FText, target, int32, sourceNum, int32, targetNum);
	UPROPERTY(BlueprintAssignable, Category = "Game")
	FOnPlayerKilled onPlayerKilled;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Music")
	float threatLevel;
	TArray<float> threats;
	UFUNCTION(Client, Reliable)
	void AddThreatLevel_Client(const float a_NewThreat);
	UFUNCTION(Client, Reliable)
	void RemoveThreatLevel_Client(const float a_NewThreat);

	// Base Stats for all characters
	UPROPERTY(VisibleAnywhere, Category = "Stats")
	UCurveFloat* healthCurve;
	UPROPERTY(VisibleAnywhere, Category = "Stats")
	UCurveFloat* healthRegenCurve;
	UPROPERTY(VisibleAnywhere, Category = "Stats")
	UCurveFloat* manaCurve;
	UPROPERTY(VisibleAnywhere, Category = "Stats")
	UCurveFloat* manaRegenCurve;
	UPROPERTY(VisibleAnywhere, Category = "Stats")
	UCurveFloat* armorCurve;
	UPROPERTY(VisibleAnywhere, Category = "Stats")
	UCurveFloat* attackSpeedCurve;

	// Use these functions to sample the above curves, these functions also check class specific overrides
	float SampleHealthCurve(float in);
	float SampleManaCurve(float in);
	float SampleArmorCurve(float in);
	float SampleAttackSpeedCurve(float in);

	// Creature level scaling logic
	// Damage done to creatures
	UPROPERTY(VisibleAnywhere, Category = "Stats")
	UCurveFloat* creatureDamageTakenCurve;
	// Damage dealt by creatures
	UPROPERTY(VisibleAnywhere, Category = "Stats")
	UCurveFloat* creatureDamageDealtCurve;

	UFUNCTION(BlueprintCallable, Category = "Key")
	bool HasKey(PlayerKeyType keyType) const;
	UFUNCTION(BlueprintCallable, Category = "Key")
	bool HasAnyKey() const;
	bool AssignKey(PlayerKeyType keyType, class ASpawnerBase* origin);
	bool ClearKey(PlayerKeyType keyType);

	// Starts a filter for a duration with a timeline to control the blending.
	UFUNCTION(BlueprintCallable, Category = "ScreenFilter")
	void StartFilter(float duration, UPostProcessComponent* filter, UCurveFloat* timeline);

	DECLARE_DYNAMIC_DELEGATE_TwoParams(FPostAction, float, value, UPostProcessComponent*, filter);

	// Starts a filter for a duration with a timeline to control the blending.
	UFUNCTION(BlueprintCallable, Category = "ScreenFilter")
	void StartFilterWithDelegate(float duration, UPostProcessComponent* filter, UCurveFloat* timeline, FPostAction action);

	// Starts a filter with a timeline to control the blending.
	UFUNCTION(BlueprintCallable, Category = "ScreenFilter")
	void EnableFilter(float duration, UPostProcessComponent* filter, UCurveFloat* timeline);

	// Stops a filter after a duration with a timeline to control the blending.
	UFUNCTION(BlueprintCallable, Category = "ScreenFilter")
	void DisableFilter(float duration, UPostProcessComponent* filter, UCurveFloat* timeline);

	// Starts a filter with a timeline to control the blending.
	UFUNCTION(BlueprintCallable, Category = "ScreenFilter")
	void EnableFilterWithDelegate(float duration, UPostProcessComponent* filter, UCurveFloat* timeline, FPostAction action);

	// Stops a filter after a duration with a timeline to control the blending.
	UFUNCTION(BlueprintCallable, Category = "ScreenFilter")
	void DisableFilterWithDelegate(float duration, UPostProcessComponent* filter, UCurveFloat* timeline, FPostAction action);

	// The ring below the player
	UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
	UStaticMeshComponent* playerCircle;

protected:
	void m_SetLevelStats() override;
private:
	// Sets the duration of the filter.
	// Creates a delagate for StopFilter.
	void SetFilterDuration(float duration, UPostProcessComponent* filter);

	// Sets the duration of the timeline.
	// Creates a delagate for StopTimeline.
	void SetTimelineDuration(float duration, UPostProcessComponent* filter);

	// Disables the filter.
	void StopFilter(UPostProcessComponent* filter, FTimerHandle handle);

	// Clears the timeline.
	void StopTimeline(UPostProcessComponent* filter, FTimerHandle handle);

	// Updates the blending weight of the filter.
	void FilterUpdate(float value, UPostProcessComponent* filter);
	void FilterUpdate(float value, UPostProcessComponent* filter, FPostAction action);

	// TODO: Add filter queue.

	bool m_levelStatsInitialized;

	UPROPERTY(Replicated)
	uint8 m_keyState;
	TMap<uint8, class ASpawnerBase*> m_keyOrigins;

	TMap<UPostProcessComponent*, FTimeline> m_filterTimelines;
	TArray<FTimerHandle> m_delegates;
};
