// Project Lab - NHTV Igad

#include "UnrealProject.h"
#include "NetworkCharacter.h"
#include "AbilityTriggerBase.h"
#include "BlueprintAbilityLibrary.h"


AAbilityTriggerBase::AAbilityTriggerBase()
{
	PrimaryActorTick.bCanEverTick = true;

	filter = EAbilityFilter::EnemyAll;
	autoDestroy = true;
	bReplicateMovement = true;
	bReplicates = true;
	duration = 1.0f;
	ticksPerSecond = 0.0f;
	m_lifeTime = 0.0f;
}

void AAbilityTriggerBase::BeginPlay()
{
	if (Role == ROLE_Authority)
	{
		if (!character)
		{
			GWERROR(L"No owner assigned to trigger " + GetName());
			this->SetActorEnableCollision(false);
			Destroy();
		}
		if (ticksPerSecond > 0)
		{
			m_tickTime = 1.0f / ticksPerSecond;
		}
		else
		{
			m_tickTime = 0.0f;
		}
	}

	//make sure the collision delegates are working
	UShapeComponent* root = Cast<UShapeComponent>(RootComponent);
	if (root != nullptr && !delegatesSet)
	{
		root->OnComponentBeginOverlap.AddDynamic(this, &AAbilityTriggerBase::OnOverlapBegin);
		root->OnComponentEndOverlap.AddDynamic(this, &AAbilityTriggerBase::OnOverlapEnd);
		delegatesSet = true;
	}
	else
		FWARNING("rootComponent is not a UShapeComponent");
	Super::BeginPlay();
	if (Role == ROLE_Authority)
		UpdateOverlaps();
}


void AAbilityTriggerBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	if (Role == ROLE_Authority)
	{
		while (m_actorsInside.Num()>0)
		{
			if (IsValid(m_actorsInside.GetData()[0]))
			{
				LeaveEvent(Cast<ANetworkCharacter>(m_actorsInside.GetData()[0]));
			}
		}
	}
	Super::EndPlay(EndPlayReason);
}

void AAbilityTriggerBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME_CONDITION(AAbilityTriggerBase, duration, COND_InitialOnly);
}

void AAbilityTriggerBase::Tick(float DeltaTime)
{
	m_lifeTime += DeltaTime;

	Super::Tick(DeltaTime);

	if(Role != ROLE_Authority || !IsValid(this))
		return;

	UpdateOverlaps();
	if(autoDestroy && m_lifeTime >= duration)
	{
		Destroy();
		return;
	}

	if(m_tickTime > 0)
	{
		m_tickTimer += DeltaTime;
		while(m_tickTimer > m_tickTime)
		{
			m_tickTimer -= m_tickTime;
			for(AActor* actorInside : m_actorsInside)
			{
				if (CollisionCheck(Cast<ANetworkCharacter>(actorInside)))
				{
					OnTriggerTick(Cast<ANetworkCharacter>(actorInside));
					NativeOnLocalTriggerTick(Cast<ANetworkCharacter>(actorInside));
				}
			}
		}
	}
}

void AAbilityTriggerBase::OnOverlapBegin(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	if(Role != ROLE_Authority)
		return;

	if(OtherActor == NULL)
	{
		FWPRINT(L"Error - wrong otherunit on OnOverlapBegin ");
		return;
	}
	if(character == NULL)
	{
		FWPRINT(L"Error - m_character NULL on OnOverlapBegin ");
		return;
	}

	ANetworkCharacter* otherUnit = Cast<ANetworkCharacter>(OtherActor);
	if(!otherUnit->GetHitable())
		return;
	if(!ULib::CheckAbilityFilter(filter, character, otherUnit))
		return;
	if(hitOnce && m_CheckActorsHit(otherUnit))
		return;

	m_actorsInside.Add(OtherActor);
	if (CollisionCheck(Cast<ANetworkCharacter>(OtherActor)))
	{
		HitEvent(otherUnit);
	}
}


void AAbilityTriggerBase::OnOverlapEnd(class AActor * OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	if(Role == ROLE_Authority)
		LeaveEvent(Cast<ANetworkCharacter>(OtherActor));
}

void AAbilityTriggerBase::HitEvent(class ANetworkCharacter* otherUnit)
{
	if (hitOnce)
		m_actorsHit.Add(otherUnit);
	OnTriggerHit(otherUnit);
}
void AAbilityTriggerBase::LeaveEvent(class ANetworkCharacter* otherActor)
{
	if (Role == ROLE_Authority)
	{
		for (auto it = (m_actorsInside.CreateIterator()); it; it++)
		{
			if (*it == otherActor)
			{
				m_actorsInside.Remove(otherActor);
				break;
			}
		}

		if (!ULib::CheckAbilityFilter(filter, character, otherActor))
			return;
		OnTriggerLeave(otherActor);
	}
}

float AAbilityTriggerBase::GetLifeTime() const
{
	return m_lifeTime;
}

float AAbilityTriggerBase::GetLifeTimeRate() const
{
	return FMath::Clamp(m_lifeTime / duration, 0.0f, 1.0f);
}

bool AAbilityTriggerBase::m_CheckActorsHit(class ANetworkCharacter* otherUnit)
{
	for (ANetworkCharacter* character : m_actorsHit)
	{
		if (character == otherUnit)
			return true;
	}
	return false;
}

bool AAbilityTriggerBase::CollisionCheck(ANetworkCharacter* otheractor)
{
	return IsValid(otheractor);
}
void AAbilityTriggerBase::OnTriggerHit_Implementation(ANetworkCharacter* actor)
{

}
void AAbilityTriggerBase::OnTriggerTick_Implementation(ANetworkCharacter* actor)
{

}
void AAbilityTriggerBase::OnTriggerLeave_Implementation(ANetworkCharacter* actor)
{

}

void AAbilityTriggerBase::OnLocalTriggerTick_Implementation(ANetworkCharacter* actor)
{

}

void AAbilityTriggerBase::NativeOnLocalTriggerTick_Implementation(ANetworkCharacter* actor)
{
	OnLocalTriggerTick(actor);
}