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


#include "BaseWeaponClass.h"

#include "AIHelpers.h"
#include "EndlessVendetta/EndlessVendettaCharacter.h"
#include "Kismet/KismetMathLibrary.h"
#include "Components/CapsuleComponent.h"
#include "Kismet/GameplayStatics.h"
#include "Engine/DamageEvents.h"
#include "EndlessVendetta/AI/EnemyCharacter.h"
#include <EndlessVendetta/EndlessVendettaGameMode.h>


// Sets default values
ABaseWeaponClass::ABaseWeaponClass()
{
	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
}

// Called when the game starts or when spawned
void ABaseWeaponClass::BeginPlay()
{
	Super::BeginPlay();
	tempWeaponDamage = WeaponDamage;
	collisionParams.AddIgnoredActor(playerInWorld);
	collisionParams.AddIgnoredActor(this);
	
	// Attempt to find the player character
	APlayerController* PlayerController = UGameplayStatics::GetPlayerController(this, 0);
	playerInWorld = Cast<AEndlessVendettaCharacter>(PlayerController->GetCharacter());
	
	endlessVendettaChar = Cast<AEndlessVendettaCharacter>(playerInWorld);

	for (UActorComponent* actorComp : playerInWorld->GetComponentsByTag(UArrowComponent::StaticClass(), FName("GunStart")))
	{
		GunStartArrow = Cast<UArrowComponent>(actorComp);
		break;
	}

	if (!playerControllerRef)
	{
		playerControllerRef = UGameplayStatics::GetPlayerController(GetWorld(), 0);
	}

	originalMagnitude = recoilMagnitude;
	originalMaxAngleLeft = recoilMaxAngleLeft;
	originalMaxAngleRight = recoilMaxAngleRight;
	originalMinMultiplier = recoilMinMultiplier;

	currentAmmoCount = MagazineSize;

	UE_LOG(LogTemp, Display, TEXT("crnnt ammo: %d"), MagazineSize);
}


// Called every frame
void ABaseWeaponClass::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	if (playerInWorld)
	{
		ApplyRecoil(DeltaTime);
	}
	if (currentPitch < 0 && bStopShooting)
	{
		float increment = currentPitch * DeltaTime * 8;
		currentPitch -= increment;
		playerInWorld->AddControllerPitchInput(-increment);
	}
}


// Recoil Handling ////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ABaseWeaponClass::GenerateRecoilVector()
{
	float angle = FMath::RandRange(recoilMaxAngleLeft, -recoilMaxAngleRight); //randomg recoil vector angle between left and right
	float recMag = recoilMagnitude * 252.f; //converting degrees to controller units
	float tempMag = -FMath::RandRange(recMag * recoilMinMultiplier, recMag); // recoil magnitude

	recoilResultYaw = FMath::Sin(FMath::DegreesToRadians(angle));
	recoilResultPitch = FMath::Cos(FMath::DegreesToRadians(angle));
	
	if (bulletCountShoot >= howMnyShotsTillRclStop)
	{
		recoilResultPitch = 0;
	}
	//scaling direction to magnitude
	recoilResultPitch *= -tempMag;
	recoilResultYaw *= tempMag;
	
	//reset recoil to 0 of curve
	recoilTime = 0;
}

void ABaseWeaponClass::ApplyRecoil(float DeltaTime)
{
	if (recoilTime < 0.3)
	{
		float amplitude = RecoilCurve->GetFloatValue(recoilTime); //get current value of curve in time
		recoilTime += DeltaTime;
		if (bulletCountShoot <= 3)
		{
			playerInWorld->AddControllerPitchInput(GetRecoilPitch(amplitude, recoilTime));
			currentPitch += GetRecoilPitch(amplitude,recoilTime);
		}
		playerInWorld->AddControllerPitchInput(FMath::RandRange(-5 * DeltaTime, 5* DeltaTime));
		GunStartArrow->AddRelativeRotation(FRotator(-GetRecoilPitch(amplitude, recoilTime), 0, 0));
		playerInWorld->AddControllerYawInput(GetRecoilYaw(amplitude, recoilTime));
		UpdateSamples(amplitude, recoilTime);
	}
}


void ABaseWeaponClass::nullSamples()
{
	recoilCurvet = 0;
	recoilCurvez1 = 0;
}

float ABaseWeaponClass::GetRecoilPitch(float Amp, float Time)
{
	//Using the trapez method and we are getting the surface under the curve, each trapezoid consist of square and right triangle
	float lower = recoilCurvez1 < Amp ? recoilCurvez1 : Amp; //get which point is common for both triangle and square of trapezoid
	//lower point
	float mult = (Time - recoilCurvet) * lower; //getting surface of square
	mult += (Time - recoilCurvet) * (Amp - recoilCurvez1) / 2.0f; //getting and adding surface of triangle
	return (recoilResultPitch * mult) / playerControllerRef->InputPitchScale_DEPRECATED; //calculating and return recoil force for current frame
}


//Same as getrecoilpitch
float ABaseWeaponClass::GetRecoilYaw(float Amp, float Time)
{
	float lower = recoilCurvez1 < Amp ? recoilCurvez1 : Amp;
	float mult = (Time - recoilCurvet) * lower;

	mult += (Time - recoilCurvet) * (Amp - recoilCurvez1) / 2.0f;
	return (recoilResultYaw * mult) / playerControllerRef->InputYawScale_DEPRECATED;
}

void ABaseWeaponClass::UpdateSamples(float Amp, float Time)
{
	recoilCurvez1 = Amp;
	recoilCurvet = Time;
}

// Fire handling //////////////////////////////////////////////////////////////////////////////////////////////////////////
void ABaseWeaponClass::ClickDetectionTimer()
{
	GetWorldTimerManager().SetTimer(timerHandle, this, &ABaseWeaponClass::Fire, 1 / FireRate, false);	
}

void ABaseWeaponClass::CancelFire()
{
	GetWorldTimerManager().ClearTimer(timerHandle);
	if (bulletCountShoot <= 0) return;
	UE_LOG(LogTemp, Display, TEXT("FireCancelled"));
	bulletCountShoot = 0;
	GunStartArrow->SetRelativeRotation(FRotator(0));
	//playerInWorld->GetController()->SetControlRotation(FRotator(0, playerInWorld->GetActorRotation().Yaw, playerInWorld->GetActorRotation().Roll));
	bStopShooting = true;
	nullSamples();
}

void ABaseWeaponClass::Fire()
{
	if(currentAmmoCount > 0)
	{
		//do damage fallof based off distance
		traceStart = GunStartArrow->GetComponentLocation();
		traceEnd = traceStart + (GunStartArrow->GetForwardVector() * BulletDistance);
		if (GetWorldTimerManager().IsTimerActive(timerHandle)) return;
		GetWorld()->LineTraceSingleByChannel(outHit, traceStart, traceEnd, ECC_Visibility, collisionParams);
		DrawDebugLine(this->GetWorld(), traceStart, traceEnd, FColor::Black , false, 0.2f, 0U, 0.2f);
		playerControllerRef->PlayerCameraManager->StartCameraShake(CameraShakeClass, 1);
		currentAmmoCount -= 1;
		GenerateRecoilVector();
		ClickDetectionTimer();
		bulletCountShoot += 1;
		bStopShooting = false;
		if (outHit.bBlockingHit)
		{
			if (outHit.Distance >= FMath::Floor(BulletDistance/2))
			{
				tempWeaponDamage = WeaponDamage / 2;
			}
			else
			{
				tempWeaponDamage = WeaponDamage;
			}
			if (!Cast<AAICharacter>(outHit.GetActor())) return;
			Cast<AAICharacter>(outHit.GetActor())->TakeDamage(tempWeaponDamage, FPointDamageEvent(), GetWorld()->GetFirstPlayerController(), this);
		}
	}
	else if(currentAmmoCount <= 0)
	{
		UE_LOG(LogTemp, Display, TEXT("No ammo, Ammo count: %d"), currentAmmoCount);
	}
	
}

void ABaseWeaponClass::WeaponScopedFire()
{
	if (endlessVendettaChar->bIsScoped)
	{
		recoilMagnitude = scopedRecoilMag; 
		recoilMaxAngleLeft = scopedMaxAngleLeft; 
		recoilMaxAngleRight = scopedMaxAngleRight; 
		recoilMinMultiplier = scopedMinMultiplier; 
		
	}
	else
	{
		recoilMagnitude = originalMagnitude;
		recoilMaxAngleLeft = originalMaxAngleLeft;
		recoilMaxAngleRight = originalMaxAngleRight;
		recoilMinMultiplier = originalMinMultiplier;
	}
}

void ABaseWeaponClass::ReloadTimer()
{
	GetWorldTimerManager().SetTimer(reloadTimerHandle, this, &ABaseWeaponClass::WeaponReload, TimeToReload, false);
	ShowReloadingWidget();
}

void ABaseWeaponClass::WeaponReload()
{
	if(MagazineSize >= currentAmmoCount)
	{
		UE_LOG(LogTemp, Display, TEXT("Weapon Reloading: mag size: %d"), MagazineSize);
		currentAmmoCount = MagazineSize;
		endlessVendettaChar->bIsReloading = false;
		HideReloadingWidget();
	}
}

void ABaseWeaponClass::Interact()
{
	if(!IsValid(this)) return;
	endlessVendettaChar->WeaponSwitcher(this);
	Cast<AEndlessVendettaGameMode>(GetWorld()->GetAuthGameMode())->SendEvent("pickup", "");
}

void ABaseWeaponClass::InteractPrompt()
{
	WeaponStatsPopUp();
}

// void ABaseWeaponClass::GetOutHit(FHitResult OutHit)
// {
// 	if(IsValid(this))
// 	{
// 		endlessVendettaChar->PrimaryWeapon->Destroy();
// 	}
// 	if (OutHit.GetActor()->ActorHasTag(FName("AssaultRifle")))
// 	{
// 		endlessVendettaChar->EquipPrimary();
// 	}
// 	AActor* HitActor = Cast<ABaseWeaponClass>(endlessVendettaChar->PrimaryWeaponClass);
// 	endlessVendettaChar->PrimaryWeapon = Cast<ABaseWeaponClass>(HitActor);
// }



 
