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

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "BoostComponent.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class SONICUNLEASHEDREMAKE_API UBoostComponent : public UActorComponent
{
	class ASonicCharacterComponent* SonicCharacter;
	GENERATED_BODY()
	//these are the delegates that we will use to play the boost sound and particles
	//these basically are the in blueprint events
	DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnBoostEvent);
	DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnAirBoostEvent);
	DECLARE_DYNAMIC_MULTICAST_DELEGATE(FBoostingEvent); // this one will tick every tick that we are boosting
	DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnStopBoostEvent);

	//events to be accessed in the Blueprints
	UPROPERTY(BlueprintAssignable, Category = "Boost")
		FOnBoostEvent OnBoostEvent;
	UPROPERTY(BlueprintAssignable, Category = "Boost")
		FOnAirBoostEvent OnAirBoostEvent;
	UPROPERTY(BlueprintAssignable, Category = "Boost")
		FOnStopBoostEvent OnStopBoostEvent;
	UPROPERTY(BlueprintAssignable, Category = "Boost")
		FBoostingEvent BoostingEvent;

public:	
	// Sets default values for this component's properties
	UBoostComponent();

protected:
	// Called when the game starts
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Boost")
		float BoostPower;

	UFUNCTION(BlueprintCallable, Category = "Boost")
		void BoostPressed();
	UFUNCTION(BlueprintCallable, Category = "Boost")
		void BoostReleased();
	
	void BoostHandler();
	void AirBoosting();
	UFUNCTION(BlueprintCallable, Category = "Boost")
		void EndAirBoostAnimation();
	UFUNCTION(BlueprintCallable, Category = "Boost")
		bool IsAllowedToBoost() const;
	
	UFUNCTION(BlueprintCallable, Category = "Boost")
		void EnableAirBoosting();
	UFUNCTION(BlueprintCallable, Category = "Boost")
		void DisableAirBoosting();
	
	UFUNCTION(BlueprintCallable, Category = "Boost")
	bool IsBoosting() const { return bIsBoosting; }
	float BoostExtraSpeedConstant;

	FTimerHandle AirBoostAnimationTimer;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Boost")
		bool bIsBoosting;
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Boost")
		bool bHasAirBoosted; //you will never actually be in the state of airboosting, because it only triggers once

};



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


#include "Sonic/BoostComponent.h"

#include "GameFramework/CharacterMovementComponent.h"
#include "Runtime/SymsLib/syms/core/generated/syms_meta_serial_ext.h"
#include "Sonic/SonicCharacterComponent.h"

// Sets default values for this component's properties
UBoostComponent::UBoostComponent()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;
	BoostPower = 100.0f;
	BoostExtraSpeedConstant = 2000.0f;
	bIsBoosting = false; //this is a toggle boolean
	bHasAirBoosted = false;
	
}


// Called when the game starts
void UBoostComponent::BeginPlay()
{
	Super::BeginPlay();
	SonicCharacter = Cast<ASonicCharacterComponent>(GetOwner());
}


// Called every frame
void UBoostComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
	BoostHandler();
}

void UBoostComponent::BoostPressed()
{
	//we play sound and particles and camera shake in the blueprint
	//todo check if we can even boost
	//todo add boost meter implementation

	//only boost if we are not currently boosting, to prevent double inputs
	if (bIsBoosting || bHasAirBoosted) return;
	
	if (SonicCharacter->SonicMovement->IsFalling())
	{
		AirBoosting();
		return;
	}
	OnBoostEvent.Broadcast();
	bIsBoosting = true;
	const float MissingSpeedFromMax = SonicCharacter->SonicMovement->GetMaxSpeed() - SonicCharacter->SonicMovement->Velocity.Size();
	const FVector BoostDirection = SonicCharacter->GetActorForwardVector() * MissingSpeedFromMax;
	SonicCharacter->SonicMovement->Velocity += BoostDirection;
	SonicCharacter->SonicMovement->MaxWalkSpeed = SonicCharacter->SonicMovement->GetMaxSpeed() + BoostExtraSpeedConstant;
	SonicCharacter->SonicMovement->StopSlide();
}


void UBoostComponent::BoostHandler()
{
	if (!bIsBoosting)
		return;
	if (!IsAllowedToBoost())
	{
		BoostReleased(); // force release boost if not allowed anymore
		return;
	}
	//boost!
	SonicCharacter->AddMovementInput(SonicCharacter->GetActorForwardVector(), 2.0f);

}

void UBoostComponent::AirBoosting()
{
	bHasAirBoosted = true;
	OnAirBoostEvent.Broadcast();
	const float MissingSpeedFromMax = SonicCharacter->GetCharacterMovement()->GetMaxSpeed() - SonicCharacter->GetCharacterMovement()->Velocity.Size();
	const FVector BoostDirection = SonicCharacter->GetActorForwardVector() * MissingSpeedFromMax;
	SonicCharacter->GetCharacterMovement()->Velocity += BoostDirection;
	//this sets the state to airboosting for 0.3 seconds, so we can play the animation
	SonicCharacter->CurrentState = EFEeSonicCharacterState::AirBoosting;
	GetWorld()->GetTimerManager().SetTimer(AirBoostAnimationTimer, this, &UBoostComponent::EndAirBoostAnimation, 0.3f, false);
	SonicCharacter->HomingAttackComponent->AirDashDisable();
	if (SonicCharacter->CurrentState == EFEeSonicCharacterState::Jumping)
	{
		SonicCharacter->CurrentState = EFEeSonicCharacterState::Falling;
	}


}

void UBoostComponent::EndAirBoostAnimation()
{
	if (SonicCharacter->CurrentState == EFEeSonicCharacterState::AirBoosting)
	{
		SonicCharacter->CurrentState = EFEeSonicCharacterState::Falling;
	}

}


bool UBoostComponent::IsAllowedToBoost() const
{
	//check if we are in the air
	const bool AirCheck = !SonicCharacter->GetCharacterMovement()->IsFalling();
	const bool SpeedCheck = SonicCharacter->GetCharacterMovement()->Velocity.Size() > 500.0f; // todo 500 is the boosting min speed, make it a variable
	return AirCheck && SpeedCheck;
}


void UBoostComponent::BoostReleased()
{
	if (!bIsBoosting)
	{
		return;
	}
	bIsBoosting = false;
	//reset the walk speed
	SonicCharacter->SonicMovement->MaxWalkSpeed = SonicCharacter->SonicMovement->GetMaxSpeed() - BoostExtraSpeedConstant;
	OnStopBoostEvent.Broadcast();
}

inline void UBoostComponent::EnableAirBoosting()
{
	bHasAirBoosted = false;
}

inline void UBoostComponent::DisableAirBoosting()
{
	bHasAirBoosted = true;
}

assume sonic unleashed / sonic generations 3d movement system.
can you finish and improve this boost component?