// Project Sword & Gun Copyright © 2021 Kevin Poretti


#include "Weapons/SNGHitscanWeapon.h"

#include "DrawDebugHelpers.h"
#include "Characters/SNGCharacterBase.h"
#include "Kismet/GameplayStatics.h"
#include "PhysicalMaterials/PhysicalMaterial.h"
#include "SwordNGun/SNGTypes.h"
#include "SwordNGun/SwordNGun.h"

static int32 DebugWeaponDrawing = 0;
FAutoConsoleVariableRef CVARDebugWeaponDrawing(TEXT("SNG.DebugWeapons"),
                        DebugWeaponDrawing, 
                        TEXT("Draw Debug Lines for Weapons"), 
                        ECVF_Cheat);

ASNGHitscanWeapon::ASNGHitscanWeapon()
{
	Range = 10000.0f;
}

void ASNGHitscanWeapon::PlayFireEffect(FVector TraceEnd)
{
	Super::PlayFireEffect(TraceEnd);

	// spawn attached particle emitter at muzzle location

	// perform tracer effect from muzzle to trace end
}

void ASNGHitscanWeapon::DoFire_Implementation()
{
	Super::DoFire_Implementation();

	DoRaycast();
}

void ASNGHitscanWeapon::DoRaycast(FRotator SpreadOffset)
{
	// Get weapon owner and check if valid
	AActor* WeaponOwner = GetOwner();
	if(!WeaponOwner)
	{
		UE_LOG(LogTemp, Error, TEXT("SNGHitscanWeapon :: Weapon does not have owner"));
		return;
	}

	// Get actor eyes viewpoint
	FVector EyesLocation;
	FRotator EyesRotation;
	WeaponOwner->GetActorEyesViewPoint(EyesLocation, EyesRotation);
	
	// Calculate final raycast direction based on actor eyes and spread offset
	// find trace end using range value or final falloff curve keyframe
	FRotator ShotRotation = EyesRotation + SpreadOffset;
	FVector ShotDirection = ShotRotation.Vector();
	FVector TraceEnd = EyesLocation + (ShotDirection * Range);

	// set up collision query params (ignore weapon and owner, trace complex, return phys material)
	FCollisionQueryParams QueryParams;
	QueryParams.AddIgnoredActor(this);
	QueryParams.AddIgnoredActor(WeaponOwner);
	QueryParams.bTraceComplex = true;
	QueryParams.bReturnPhysicalMaterial = true;
	
	// do line trace by channel
	// if hit
	//		get hit actor
	//		get surface type
	//		apply (point) damage base on surface type
	//		play impact effect
	// play fire effect
	// if debug drawing is turned on
	//		draw line showing the actual raycast
	//		draw sphere at impact point

	EPhysicalSurface SurfaceType = SurfaceType_Default;
	FHitResult HitResult;
	bool IsHitSuccessful = GetWorld()->LineTraceSingleByChannel(HitResult, EyesLocation, TraceEnd, COLLISION_WEAPON, QueryParams);
	if(IsHitSuccessful)
	{
		AActor* HitActor = HitResult.GetActor();

		SurfaceType = UPhysicalMaterial::DetermineSurfaceType(HitResult.PhysMaterial.Get());
		//int ActualDamage = CalculateWeaponDamage();
		// NOTE(kevin): This won't work once we have weapon falloff curves. Might want to separate damage from HitData
		// eventually
		int ActualDamage = HitData.Damage; 
		if(SurfaceType == SNG_SURFACE_FLESH_VULNERABLE)
		{
			ActualDamage *= VulnerableDamageMultiplier;
		}

		FSNGDamageEvent DamageEvent;
		DamageEvent.DamageTypeClass = DamageType;
		DamageEvent.HitData = HitData;
		DamageEvent.SurfaceType = SurfaceType;
		DamageEvent.ImpactPoint = HitResult.ImpactPoint;
		HitActor->TakeDamage(ActualDamage, DamageEvent, WeaponOwner->GetInstigatorController(), this);
		
		if(HitData.HistopApplicationType == ESNGHitstopApplicationType::BOTH ||
			HitData.HistopApplicationType == ESNGHitstopApplicationType::ATTACKER_ONLY)
		{
			ASNGCharacterBase* OwnerAsChar = Cast<ASNGCharacterBase>(GetOwner());
			OwnerAsChar->ApplyHitStop(HitData.StopTime, HitData.SlowTime);
		}
		
		PlayImpactEffects(SurfaceType, HitResult.ImpactPoint);
	}

	PlayFireEffect(IsHitSuccessful ? HitResult.ImpactPoint : TraceEnd);

	if(DebugWeaponDrawing)
	{
		DrawDebugLine(GetWorld(), EyesLocation, TraceEnd, FColor::Red, false, 1.0f, 0, 1.0f);
		DrawDebugSphere(GetWorld(), HitResult.ImpactPoint, 5.0f, 32, FColor::Green, false, 1.0f, 0, 1.0f);
	}
}


