// Project Lab - NHTV Igad

#include "UnrealProject.h"
#include "AbilityEventGroup.h"
#include "NetworkCharacter.h"
#include "NPCBase.h"

AAbilityEventGroup::AAbilityEventGroup()
{
 	// 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;
	channelEvent = false;
	stunWhileChannel = true;
	progressionType = EAbilityEventGroupProgressionType::Continue;
	duration = 1.0f;
	m_endEventSend = false;
	m_hasBeenDestroyed = false;
	allowRotateWhileChannel = false;
}

void AAbilityEventGroup::BeginPlay()
{
	check(Role == ROLE_Authority);
	Super::BeginPlay();
	m_life = 0.0f;

	if(!character || character->IsActorBeingDestroyed())
	{
		m_MoveToNextGroup();
	}
	else
	{
		// Handle the destruction of the caster
		character->OnDestroyed.AddDynamic(this, &AAbilityEventGroup::m_OnCharacterDestroyed);
	}
}
void AAbilityEventGroup::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	check(Role == ROLE_Authority);
	if(EndPlayReason == EEndPlayReason::Destroyed)
	{
		Super::EndPlay(EndPlayReason);
		AAbilityEventGroup* nextGroup = nullptr;
		if(m_nextGroups.Num() > 0)
		{
			//GWPRINT(L"Move next");
			nextGroup = SpawnSequence(character, abilityInfo, abilityState, m_nextGroups);
		}
		m_SendEndEvent(nextGroup);
	}
}
void AAbilityEventGroup::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	if(IsPendingKill())
		return;

	m_life += DeltaTime;
	if(progressionType == EAbilityEventGroupProgressionType::Continue)
	{
		if(m_life >= duration || m_continue)
			m_MoveToNextGroup();
	}
	else
	{
		if(m_continue)
			m_MoveToNextGroup();
	}
}

void AAbilityEventGroup::m_OnCharacterDestroyed()
{
	m_MoveToNextGroup();
}
void AAbilityEventGroup::m_MoveToNextGroup()
{
	if(m_hasBeenDestroyed)
		return;
	m_hasBeenDestroyed = true;
	if(IsValid(this) && !IsPendingKillPending())
		Destroy();
}

void AAbilityEventGroup::m_SendEndEvent(AAbilityEventGroup* next)
{
	if(!m_endEventSend)
	{
		onAbilityEventGroupEnded.Broadcast(abilityInfo, this, next);
		m_endEventSend = true;
	}
}

void AAbilityEventGroup::Interrupt()
{
	//GPRINT("Interrupting ability");
	if(IsPendingKill())
		return;
	m_nextGroups.Empty(); // Don't execute any follow up events
	m_hasBeenDestroyed = true;
}

void AAbilityEventGroup::TransitionTo(class AAbilityEventGroup* newGroup)
{
	check(newGroup);
	check(abilityInfo);
	check(abilityState);
	newGroup->character = character;
	newGroup->abilityInfo = abilityInfo;
	newGroup->abilityState = abilityState;
	newGroup->m_nextGroups = m_nextGroups;
	m_nextGroups.Empty();

	m_SendEndEvent(newGroup);

	//GWPRINT(L"TransitionTo next");
	m_MoveToNextGroup();
}

void AAbilityEventGroup::NextGroup()
{
	m_continue = true;
}

ANetworkCharacter* AAbilityEventGroup::GetTarget()
{
	ANPCBase* creature = Cast<ANPCBase>(character);
	if(creature)
	{
		return creature->target;
	}
	return nullptr;
}

float AAbilityEventGroup::GetLifetimeRate() const
{
	float rate = m_life / duration;
	return FMath::Fmod(rate, 1.0f + FLT_EPSILON);
}
float AAbilityEventGroup::GetLifetime() const
{
	return m_life;
}

AAbilityEventGroup* AAbilityEventGroup::SpawnSequence(ANetworkCharacter* character, class UAbilityInfo* abilityInfo, class AAbilityState* abilityState, TArray<TSubclassOf<class AAbilityEventGroup>> groups)
{
	UWorld* world = character->GetWorld();
	check(world);
	check(groups.Num() > 0);

	AAbilityEventGroup* group = world->SpawnActorDeferred<AAbilityEventGroup>(groups[0], FTransform::Identity);
	check(group); 
	check(abilityInfo);
	check(abilityState);

	group->character = character;
	group->abilityState = abilityState;
	group->abilityInfo = abilityInfo;
	groups.RemoveAt(0);
	group->m_nextGroups = groups;

	UGameplayStatics::FinishSpawningActor(group, FTransform::Identity);

	return group;
}