//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
///			Copyright 2019 (C) Bruno Xavier B. Leite
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#include "PoolSpawnOptions.h"
#include "GameFramework/ProjectileMovementComponent.h"

#include "PooledProjectile.generated.h"

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

class UObjectPool;
class APooledPawn;
class APooledActor;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOBJP_ProjectileStop, const FHitResult&, ImpactResult);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOBJP_ProjectileBounce, const FHitResult&, ImpactResult, const FVector&, ImpactVelocity);

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pooled Projectile Component

///  Pooled Projectile Template;
/// This Projectile Component works better with bullet Actors that are generated by an Object-Pool Component.
/// This Component will NOT work properly with 'Instantiate on Demand' feature, it is dependent on Engine's default Projectile Component.
UCLASS(ClassGroup = Synaptech, Category = "Movement", meta = (BlueprintSpawnableComponent, DisplayName = "Pool Projectile Movement", ShortTooltip = "Pooled Projectile Component Class."))
class OBJPOOL_API UPooledProjectile : public UActorComponent {
	GENERATED_BODY()
	//
	UPooledProjectile(const FObjectInitializer& OBJ);
	friend class APooledActor;
	friend class UObjectPool;
	friend class APooledPawn;
private:
	UPROPERTY()
	UProjectileMovementComponent* Movement;
protected:
	UPROPERTY()
	APooledActor* AOwner;
	//
	UPROPERTY()
	APooledPawn* POwner;
	//
	UPROPERTY()
	UPrimitiveComponent* Primitive;
public:
	virtual void PostInitProperties() override;
	//
	//
	/// Manually fire this Projectile, instead of relying on Component Initialization.
	UFUNCTION(Category = "Projectile", BlueprintNativeEvent, meta = (DisplayName = "Shoot", Keywords = "shoot projectile"))
	void Shoot(); virtual void Shoot_Implementation();
	//
	/// Manually stops this Projectile, instead of relying on Component Destruction.
	UFUNCTION(Category = "Projectile", BlueprintNativeEvent, meta = (DisplayName = "Break", Keywords = "break projectile"))
	void Break(); virtual void Break_Implementation();
	//
	/// Returns reference to the Movement Component that is generated at runtime, if there's any.
	UFUNCTION(Category = "Projectile", BlueprintCallable, meta = (DisplayName = "Get Projectile Movement Component", Keywords = "get projectile movement"))
	UProjectileMovementComponent* GetMovementComponent() const;
	//
	//
	UFUNCTION()
	virtual void ProjectileStop(const FHitResult &HitResult);
	//
	UFUNCTION()
	virtual void ProjectileBounce(const FHitResult &HitResult, const FVector &Velocity);
	//
	//
	/// Projectile Movement Component Interface:
	//
	/// (Velocity) Speed Vector.
	UPROPERTY(Category = "Projectile", EditAnywhere, BlueprintReadWrite, meta = (DisplayName = "Velocity"))
	FVector Direction;
	//
	/// Initial Speed of Projectile.
	UPROPERTY(Category = "Projectile", EditAnywhere, BlueprintReadWrite)
	float InitialSpeed;
	//
	/// Limit Speed of Projectile.
	UPROPERTY(Category = "Projectile", EditAnywhere, BlueprintReadWrite)
	float MaxSpeed;
	//
	UPROPERTY(Category = "Projectile", EditAnywhere, BlueprintReadWrite)
	bool UpdateOnlyIfRendered;
	//
	UPROPERTY(Category = "Projectile", EditAnywhere, BlueprintReadWrite)
	uint32 RotationFollowsVelocity:1;
	//
	UPROPERTY(Category = "Projectile", EditAnywhere, BlueprintReadWrite)
	uint32 InitialVelocityInLocalSpace:1;
	//
	UPROPERTY(Category = "Projectile", EditAnywhere, BlueprintReadWrite)
	float ProjectileGravityScale;
	//
	UPROPERTY(Category = "Projectile Bounces", EditAnywhere, BlueprintReadWrite)
	uint32 ShouldBounce:1;
	//
	UPROPERTY(Category = "Projectile Bounces", EditAnywhere, BlueprintReadWrite)
	uint32 BounceAngleAffectsFriction:1;
	//
	UPROPERTY(Category = "Projectile Bounces", EditAnywhere, BlueprintReadWrite, meta = (ClampMin="0", UIMin="0"))
	float Bounciness;
	//
	UPROPERTY(Category = "Projectile Bounces", EditAnywhere, BlueprintReadWrite, meta = (ClampMin="0", UIMin="0"))
	float Friction;
	//
	UPROPERTY(Category = "Projectile Bounces", EditAnywhere, BlueprintReadWrite)
	float BounceVelocityStopSimulatingThreshold;
	//
	UPROPERTY(Category = "Projectile Simulation", EditAnywhere, BlueprintReadWrite)
	uint32 ForceSubStepping:1;
	//
	UPROPERTY(Category = "Projectile Simulation", EditAnywhere, BlueprintReadWrite, meta = (ClampMin="1", ClampMax="25", UIMin="1", UIMax="25"))
	int32 MaxSimulationIterations;
	//
	UPROPERTY(Category = "Projectile Simulation", EditAnywhere, BlueprintReadWrite, meta = (ClampMin="0.0166", ClampMax="0.50", UIMin="0.0166", UIMax="0.50"))
	float MaxSimulationTimeStep;
	//
	UPROPERTY(Category = "Homing", EditAnywhere, BlueprintReadWrite)
	uint32 IsHomingProjectile:1;
	//
	//
	/// Event called every time this Projectile bounces.
	UPROPERTY(Category = "Object Pool", BlueprintAssignable)
	FOBJP_ProjectileBounce OnProjectileBounce;
	//
	/// Event called every time this Projectile stops trajectory.
	UPROPERTY(Category = "Object Pool", BlueprintAssignable)
	FOBJP_ProjectileStop OnProjectileStop;
	//
	//
	/* If you don't want to set Homing Target Component manually at runtime, but still needs Homing Projectile,
	you can set here an Actor as target and its Root Component will be set as Homing Component by the Pool after spawn.
	If you don't want to use the Root, then leave this empty and set Homing Target manually instead. */
	UPROPERTY(Category = "Homing", EditAnywhere, BlueprintReadWrite)
	AActor* HomingTarget;
	//
	UPROPERTY(Category = "Homing", VisibleInstanceOnly, BlueprintReadWrite)
	TWeakObjectPtr<USceneComponent> HomingTargetComponent;
	//
	UPROPERTY(Category = "Homing", EditAnywhere, BlueprintReadWrite)
	float HomingAccelerationMagnitude;
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////