// Project Lab - NHTV Igad

#include "UnrealProject.h"
#include "Modifier.h"

#include "NetworkCharacter.h"
#include "NativeModifiers.h"

ModifierManager::ModifierManager(class ANetworkCharacter* character)
	: m_character(character)
{
	check(m_character);
}
ModifierManager::~ModifierManager()
{
	ClearModifiers(false);
}

void ModifierManager::ClearModifiers(bool shouldCallEnd)
{
	for (AModifier* modifier : m_modifiers)
	{
		if (shouldCallEnd)
			modifier->m_started = false;
		modifier->Destroy();
	}
	m_modifiers.SetNum(0);
	RecalculateCharacter();
}
void ModifierManager::Tick(float DeltaSeconds)
{
	for (int32 i = 0; i < m_modifiers.Num();)
	{
		AModifier* modifier = m_modifiers[i];
		if (modifier != nullptr && IsValid(modifier))
		{
			if (modifier->ShouldBeRemoved() || modifier->m_forceDestroy)
			{
				modifier->Destroy();
				m_modifiers.RemoveAt(i);
				RecalculateCharacter();
				continue;
			}
			else
			{
				modifier->Tick(DeltaSeconds);
			}
		}
		else
		{
			m_modifiers.RemoveAt(i);
			if (modifier != 0)
			{
				GERROR("Modifier \"" + modifier->GetName() + "\" was destroyed, but not in the usual way");
			}
			RecalculateCharacter();
			continue;
		}
		i++;
	}
}

AModifier* ModifierManager::AddModifier(TSubclassOf<class AModifier> buffClass, float duration)
{
	if (!buffClass)
	{
		FWERROR(L"Invalid modifier class");
		return nullptr;
	}
	UWorld* world = m_character->GetWorld();
	check(world);
	AModifier* modifier = world->SpawnActor<AModifier>(buffClass);
	modifier->m_Init(m_character, duration);
	return AddModifier(modifier);
}
AModifier* ModifierManager::AddModifier(AModifier* modifierInstance)
{
	if (!modifierInstance)
	{
		FWERROR(L"Invalid buff added");
		return nullptr;
	}
	m_modifiers.Add(modifierInstance);
	m_modifiers.Sort([](const AModifier& i, const AModifier& j)->bool { return i > j; });
	modifierInstance->m_Start();
	RecalculateCharacter();
	return modifierInstance;
}

TArray<AModifier*> ModifierManager::GetModifiersOfClass(TSubclassOf<class AModifier> modifierClass)
{
	TArray<AModifier*> ret;
	for (AModifier* mod : m_modifiers)
	{
		if (mod->IsA(modifierClass))
		{
			ret.Add(mod);
		}
	}
	return ret;
}
void ModifierManager::BroadcastManaDrainFailed()
{
	for(AModifier* modifier : m_modifiers)
	{
		if (modifier != nullptr && IsValid(modifier))
		{
			modifier->onManaDrainFailed.Broadcast();
		}
	}
}

//reset all modifiable stats so that modifiers can recalculate them
void ModifierManager::m_Reset()
{
	m_character->ResetModifiers();
}
void ModifierManager::RecalculateCharacter()
{
	m_Reset();
	for (AModifier* it : m_modifiers)
	{
		it->ModifyCharacter();
	}
	m_character->CheckStatsOverflow();
}


AModifier::AModifier()
{
	PrimaryActorTick.bCanEverTick = true;
	bReplicates = true;
	lifeTime = 0;
	ticksPerSecond = 0;
	m_dontDestroy = false;
	RootComponent = CreateDefaultSubobject<USceneComponent>("Root");
}

bool AModifier::operator < (const AModifier& other) const
{
	return priority < other.priority;
}
bool AModifier::operator > (const AModifier& other) const
{
	return priority > other.priority;
}


void AModifier::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	if (m_started)
		ModifierEnd();
}

void AModifier::ModifyCharacter()
{
}
bool AModifier::ShouldBeRemoved() const
{
	return (lifeTime <= 0.0f) && !m_dontDestroy;
}
void AModifier::ForceDestroy()
{
	m_forceDestroy = true;
}
void AModifier::ModifierTick()
{
}

void AModifier::ModifierStart()
{

}

void AModifier::ModifierEnd()
{

}
bool AModifier::OnDamage(int32& damage, ANetworkCharacter* damageDealer)
{
	return true;
}
bool AModifier::OnDealDamage(int32& damage, ANetworkCharacter* damageTaker)
{
	return true;
}
void AModifier::OnStandardAttack(ANetworkCharacter* character)
{
	return;
}
void AModifier::AfterDamage(int32)
{
	return;
}

float AModifier::m_MultiplyEffect(float value, EffectType effectType, bool positive)
{
	switch (effectType)
	{
	case EffectType::additive:
	{
		if (!positive)
			return value * target->m_negativeEffectMultiplier;
		else
			return value * target->m_positiveEffectMultiplier;
	}
	case EffectType::multiplicative:
	{
		if (!positive)
			return value * (1.0f/target->m_negativeEffectMultiplier);
		else
			return value * target->m_positiveEffectMultiplier;
	}
	default:
		return 0.0f;
	}
}

//character modification functions
void AModifier::m_AddMaxMana(int32 maxMana)
{
	if (IsValid(target))
	{
		maxMana = m_MultiplyEffect(maxMana, additive, maxMana>0);
		target->m_maxMana += maxMana;
		target->AddMana(maxMana);
	}
}
void AModifier::m_MultiplyManaRegenMultiplier(float manaRegenMultiplier)
{
	if (IsValid(target))
	{
		manaRegenMultiplier = m_MultiplyEffect(manaRegenMultiplier, multiplicative, manaRegenMultiplier>1);
		target->m_manaRegenMultiplier *= manaRegenMultiplier;
	}
}
void AModifier::m_MultiplyManaUsageMultiplier(float manaUsageMultiplier)
{
	if (IsValid(target))
	{
		manaUsageMultiplier = m_MultiplyEffect(manaUsageMultiplier, multiplicative, manaUsageMultiplier>1);
		target->m_manaUsageMultiplier *= manaUsageMultiplier;
	}
}
void AModifier::m_AddBlockedMana(int32 blockedMana)
{
	if (IsValid(target))
	{
		if (target->m_blockedMana + blockedMana > target->m_maxMana)
		{
			ModifierManager* mod = character->GetModifierManager();
			if (mod != nullptr)
				mod->BroadcastManaDrainFailed();
		}
		else
			target->m_blockedMana += blockedMana;
	}
}
void AModifier::m_AddDamageMultiplier(float multiplier)
{
	if (IsValid(target))
	{
		multiplier = m_MultiplyEffect(multiplier, additive, multiplier > 0);
		target->m_damageMultiplier += multiplier;
	}
}
void AModifier::m_AddMaxHealth(int32 health)
{
	if (IsValid(target))
	{
		health = m_MultiplyEffect(health, additive, health >0);
		target->m_maxHealth += health;
	}
}
void AModifier::m_AddMovementSpeedMultiplier(float multiplier)
{
	if (IsValid(target))
	{
		multiplier = m_MultiplyEffect(multiplier, multiplicative, multiplier > 0);
		target->GetCharacterMovement()->MaxWalkSpeed *= multiplier;
	}
}
void AModifier::m_AddIgnoreArmor(float normalizedPercentage)
{
	if (IsValid(target))
	{
		normalizedPercentage = m_MultiplyEffect(normalizedPercentage, additive, normalizedPercentage > 0);
		target->m_ignoreArmor += normalizedPercentage;
	}
}
void AModifier::m_AddArmor(float armor)
{
	if (IsValid(target))
	{ 
		armor = m_MultiplyEffect(armor, additive, armor > 0);
		target->m_armor += armor;
	}
}

void AModifier::m_MultiplyArmor(float armor)
{
	if (IsValid(target))
	{
		armor = m_MultiplyEffect(armor, multiplicative, armor > 1);
		target->m_armor *= armor;
	}
}
void AModifier::m_MultiplyAttackDamage(float damageMultiplier)
{
	if (IsValid(target))
	{
		damageMultiplier = m_MultiplyEffect(damageMultiplier, multiplicative, damageMultiplier>1);
		FPRINT(damageMultiplier);
		target->m_attackDamageMultiplier *= damageMultiplier;
	}
}
void AModifier::m_AddMagicDamage(float damageMultiplier)
{
	if (IsValid(target))
	{
		damageMultiplier = m_MultiplyEffect(damageMultiplier, multiplicative, damageMultiplier > 1);
		target->m_magicDamageMultiplier *= damageMultiplier;
	}
}
void AModifier::m_AddAttackSpeed(float amount)
{
	if (IsValid(target))
	{
		amount = m_MultiplyEffect(amount, additive, amount> 0);
		target->m_attackSpeed += amount;
	}
}
void AModifier::m_AddAttackSpeedMultiplier(float multiplier)
{	
	if (IsValid(target))
	{
		multiplier = m_MultiplyEffect(multiplier, multiplicative, multiplier > 1);
		target->m_attackSpeed *= multiplier;
	}
}
void AModifier::m_AddCooldownReduction(float amount)
{
	if (IsValid(target))
	{
		amount = m_MultiplyEffect(amount, additive, amount > 0);
		target->m_cooldownReduction += amount;
	}
}
void AModifier::m_AddCooldownReductionMultiplier(float multiplier)
{
	if (IsValid(target))
	{
		multiplier = m_MultiplyEffect(multiplier, multiplicative, multiplier > 1);
		target->m_cooldownReduction *= multiplier;
	}
}
void AModifier::m_MultiplyDamageTakenMultiplier(float multiplier)
{
	if (IsValid(target))
	{
		multiplier = m_MultiplyEffect(multiplier, multiplicative, multiplier < 0);
		target->m_damageTakenMultiplier *= multiplier;
	}
}
void AModifier::m_MultiplyPositiveEffectMultiplier(float multiplier)
{
	if (IsValid(target))
		target->m_positiveEffectMultiplier *= multiplier;
}
void AModifier::m_MultiplyNegativeEffectMultiplier(float multiplier)
{
	if (IsValid(target))
		target->m_negativeEffectMultiplier *= multiplier;
}
void AModifier::m_addChannelMovementSpeedMultiplier(float channelMovementSpeedMultiplier)
{
	if (IsValid(target))
	{
		channelMovementSpeedMultiplier = m_MultiplyEffect(channelMovementSpeedMultiplier, additive, channelMovementSpeedMultiplier > 0);
		target->m_castingMovementspeedMultiplier += channelMovementSpeedMultiplier;
	}
}
void AModifier::m_MultiplyMagicDamageMultiplier(float multiplier)
{
	if (IsValid(target))
	{
		multiplier = m_MultiplyEffect(multiplier, additive, multiplier> 0);
		target->m_magicDamageMultiplier *= multiplier;
	}
}

void AModifier::m_AddStunnedState()
{
	if (IsValid(target))
		if (target->canBeStunned)
		{
			target->m_stunned = true;
			target->m_InteruptSpellcasting();
		}
}

void AModifier::m_AddSilencedState()
{
	if (IsValid(target))
		if (target->canBeSilenced)
		{
			target->m_silenceCount++;
			target->m_silenced = true;
			target->m_InteruptSpellcasting();
		}
}

void AModifier::m_RemoveSilencedState()
{
	if (IsValid(target))
	{
		target->m_silenceCount--;
		if (target->m_silenceCount == 0)
		{
			target->m_silenced = false;
		}
		else if (target->m_silenceCount < 0)
		{
			FERROR("m_silenceCount <0 in removeSilenceState");
		}
	}
}
void AModifier::m_Start()
{
	if (!m_started)
	{
		if (IsValid(target))
		{
			if (ticksPerSecond <= 0)
				m_tickRate = 0.0f;
			else
				m_tickRate = 1.0f / ticksPerSecond;
			m_started = true;
			if (lifeTime == 0)
			{
				m_dontDestroy = true;
			}
			ModifierStart();
		}
		else
		{
			GWERROR(L"Can't start modifier " + GetName() + " target not set");
		}
	}
	else
	{
		GWERROR(L"modifier started twice");
	}
}
void AModifier::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	if (!IsValid(target) || !m_started)
		return;
	if (ShouldBeRemoved() || m_forceDestroy)
		return;
	if (m_tickRate > 0.0f)
	{
		m_tickTimer += DeltaTime;
		while (m_tickTimer >= m_tickRate)
		{
			ModifierTick();
			m_tickTimer -= m_tickRate;
		}
	}
	lifeTime -= DeltaTime;
}
void AModifier::m_Init(class ANetworkCharacter* a_character, float duration)
{
	target = a_character;
	lifeTime = duration;
}
