// Project Sword & Gun Copyright © 2021 Kevin Poretti

#include "SNGTypes.generated.h"
#pragma once

#define SNG_SURFACE_DEFAULT				SurfaceType_Default
#define SNG_SURFACE_FLESH				SurfaceType1
#define SNG_SURFACE_FLESH_VULNERABLE	SurfaceType2

UENUM(BlueprintType)
enum class ESNGReactionType : uint8
{
	NONE,
	WALL_SPLAT,
	WALL_BOUNCE,
	GROUND_SLIDE,
	GROUND_BOUNCE,
	HARD_KNOCKDOWN,
	MAX_REACTION_TYPES,
};

UENUM(BlueprintType)
enum class ESNGLaunchType : uint8
{
	NONE,
	ONLY_IN_AIR,
	ALWAYS,
	MAX_LAUNCH_TYPES,
};

UENUM(BlueprintType)
enum class ESNGLaunchDirectionType : uint8
{
	NONE,
	USE_CAUSER_ROTATION,
	USE_LOOK_AT_ROTATION,
	MAX_LAUNCH_DIR_TYPES,
};

UENUM(BlueprintType)
enum class ESNGHitstopApplicationType : uint8
{
	NONE,
	ATTACKER_ONLY,
	VICTIM_ONLY,
	BOTH,
};

USTRUCT(BlueprintType)
struct FSNGHitData
{
	GENERATED_BODY()
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	float Damage;

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	float StunTime;

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	ESNGHitstopApplicationType HistopApplicationType;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	float StopTime;

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	float SlowTime;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	ESNGReactionType ReactionType;

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	ESNGLaunchType LaunchType;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	float LaunchForce;

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	ESNGLaunchDirectionType LaunchDirectionType;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	FVector LaunchDirection;

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	float TraceRadius;
};

/** Damage subclass that handles damage with game specific hit properties */
USTRUCT()
struct SWORDNGUN_API FSNGDamageEvent : public FDamageEvent
{
	GENERATED_BODY()

	UPROPERTY()
	FSNGHitData HitData;

	UPROPERTY()
	TEnumAsByte<EPhysicalSurface> SurfaceType;

	UPROPERTY()
	FVector ImpactPoint;
	
	/** ID for this class. NOTE this must be unique for all damage events. */
	static const int32 ClassID = 3;

	virtual int32 GetTypeID() const override { return FSNGDamageEvent::ClassID; };
	virtual bool IsOfType(int32 InID) const override { return (FSNGDamageEvent::ClassID == InID) || FDamageEvent::IsOfType(InID); };

	// NOTE(kevin) do we need this??
	/** Simple API for common cases where we are happy to assume a single hit is expected, even though damage event may have multiple hits. */
	virtual void GetBestHitInfo(AActor const* HitActor, AActor const* HitInstigator, struct FHitResult& OutHitInfo, FVector& OutImpulseDir) const override;

	FSNGDamageEvent()
	{}
};

UENUM(BlueprintType)
enum class ESNGEventType : uint8
{
	NONE,
	CHARACTER_COMMAND,
	DAMAGE,
	MAX_EVENT_TYPES,
};

USTRUCT(BlueprintType)
struct FSNGEventData
{
	GENERATED_BODY()

	FSNGEventData()
	{
	}
};

USTRUCT(BlueprintType)
struct FSNGCharacterCommandData : public FSNGEventData
{
	GENERATED_BODY()

	UPROPERTY(BlueprintReadOnly)
	FName CommandName;
	
	FSNGCharacterCommandData()
		: CommandName(NAME_None)
	{
	}
};

USTRUCT(BlueprintType)
struct FSNGDamageData : public FSNGEventData
{
	GENERATED_BODY()

	UPROPERTY(BlueprintReadOnly)
	FSNGHitData HitData;

	UPROPERTY(BlueprintReadOnly)
	AController* EventInstigator;

	UPROPERTY(BlueprintReadOnly)
    AActor* DamageCauser;
	
	UPROPERTY(BlueprintReadOnly)
	TEnumAsByte<EPhysicalSurface> SurfaceType;

	UPROPERTY(BlueprintReadOnly)
	FVector ImpactPoint;
	
	FSNGDamageData()
	{
	}
};

// discriminated union
USTRUCT(BlueprintType)
struct FSNGEvent
{
	GENERATED_BODY()

	UPROPERTY(BlueprintReadOnly)
	ESNGEventType EventType;

	union
	{
		FSNGCharacterCommandData CommandData;
		FSNGDamageData DamageData;
	};
	
	FSNGEvent()
		: EventType()
		, CommandData()
		, DamageData()
	{
	}
};



