// Project Lab - NHTV Igad

#include "UnrealProject.h"

#include "NetworkCharacter.h"
#include "NetworkPlayer.h"
#include "NativeModifiers.h"
#include "EffectFunctionLibrary.h"
#include "Effect.h"

#define standardTicksPerSecond 20
//SpeedModifier
ASpeedModifier::ASpeedModifier()
{

}

void ASpeedModifier::ModifyCharacter()
{
	UCharacterMovementComponent* characterMovement = target->GetCharacterMovement();
	characterMovement->MaxWalkSpeed *= speedMultiplier;
}

//RegenModifier
ARegenModifier::ARegenModifier()
{
	ticksPerSecond = 5;
}

bool ARegenModifier::ShouldBeRemoved() const
{
	return false;
}

void ARegenModifier::ModifierTick()
{
	if ( m_noDamageTime < target->GetTimeSinceDamage())
		target->AddHealth(regenPerTick);
}

//RegenModier
AManaRegenModifier::AManaRegenModifier()
{
	ticksPerSecond = standardTicksPerSecond;
}

bool AManaRegenModifier::ShouldBeRemoved() const
{
	return false;
}

void AManaRegenModifier::ModifierTick()
{
	target->AddMana(regenPerTick);
}

//DOTModifier
ADOTModifier::ADOTModifier()
{
	ticksPerSecond = 3;
}

void ADOTModifier::ModifierTick()
{
	if (IsValid(target))
		target->DealDamage(this, damagePerTick, 0.0f);
}

//ADModifier
AADModifier::AADModifier()
{

}

void AADModifier::ModifyCharacter()
{
	float add = ScaleGraphCurve(scalingGraph);
	m_MultiplyAttackDamage(add);
}
//ADModifier
AAPModifier::AAPModifier()
{

}

void AAPModifier::ModifyCharacter()
{
	float add = ScaleGraphCurve(scalingGraph);
	m_AddMagicDamage(add);
}

//MaxHealthModifier
AMaxHealthModifier::AMaxHealthModifier()
{
}

void AMaxHealthModifier::ModifyCharacter()
{
	//m_AddMaxHealth(bonusMaxHealth);
	float add = ScaleGraphCurve(scalingGraph);
	m_AddMaxHealth(add);
}

//AConstMaxHealthModifier
AConstMaxHealthModifier::AConstMaxHealthModifier()
{
}

void AConstMaxHealthModifier::ModifyCharacter()
{
	m_AddMaxHealth(add);
}

//BlockManaModifier
ABlockManaModifier::ABlockManaModifier()
{

}

void ABlockManaModifier::ModifyCharacter()
{
	m_AddBlockedMana(Mana);
}

//ManaPerSecModifier
AManaDrainModifier::AManaDrainModifier()
{
	ticksPerSecond = standardTicksPerSecond;
}

void AManaDrainModifier::ModifierTick()
{
	if (ManaPerTick > target->GetMana())
	{
		ModifierManager* mod = character->GetModifierManager();
		if (mod != nullptr)
			mod->BroadcastManaDrainFailed();
	}
	else
	{
		target->RemoveMana(ManaPerTick);
	}
}


//ManaPerSecModifier
AManaDrainCurveModifier::AManaDrainCurveModifier()
{
	ticksPerSecond = standardTicksPerSecond;
}

void AManaDrainCurveModifier::ModifierTick()
{
	float mana = ScaleGraphCurve(ManaPerTick);
	if (mana > target->GetMana())
	{
		ModifierManager* mod = character->GetModifierManager();
		if (mod != nullptr)
			mod->BroadcastManaDrainFailed();
	}
	else
	{
		target->RemoveMana(mana);
	}
}

//HealthModifier
AHealModifier::AHealModifier()
{
	ticksPerSecond = standardTicksPerSecond;
}

void AHealModifier::ModifierTick()
{
	target->AddHealth(health);
	m_shouldBeRemoved = true;
}

bool AHealModifier::ShouldBeRemoved() const
{
	return m_shouldBeRemoved;
}

//stunModifier
static TSubclassOf<AEffect> stunEffectType;
AStunModifier::AStunModifier()
{
	stunEffectType = ConstructorHelpers::FClassFinder<AEffect>(TEXT("/Game/Assets/Art/Effects/FX_Stunned")).Class;
}
void AStunModifier::ModifierStart()
{
	if (target->CanBeStunned() && lifeTime > 0.0f)
		UEffectFunctionLibrary::CreateEffect(target, stunEffectType, target,0.0f, lifeTime);
}
void AStunModifier::ModifierEnd()
{
}
void AStunModifier::ModifierTick()
{
}
void AStunModifier::ModifyCharacter()
{
	m_AddStunnedState();
}
//stunModifier

ASilenceModifier::ASilenceModifier()
{
}
void ASilenceModifier::ModifierStart()
{
	m_AddSilencedState();
}


void ASilenceModifier::ModifierEnd()
{
	m_RemoveSilencedState();
}
//ADamageTakenModifier
ADamageTakenModifier::ADamageTakenModifier()
{

}

void ADamageTakenModifier::ModifyCharacter()
{
	m_MultiplyDamageTakenMultiplier(damageTakenMultiplier);
}


bool ADamageTakenModifier::ShouldBeRemoved() const
{
	return false;
}

//AVisibilityModifier
AVisibilityModifier::AVisibilityModifier()
{

}

bool AVisibilityModifier::ShouldBeRemoved() const
{
	return false;
}

void AVisibilityModifier::ModifierTick()
{
	m_timer -= m_tickRate;
	if (m_timer < 0 && visibleTime > 0 && visibleBreak> 0)
	{
		if (m_visible)
		{
			m_timer = visibleBreak;
			target->m_visible = false;
		}
		else
		{
			m_timer = visibleTime;
			target->m_visible = true;
		}

		m_visible = !m_visible;
	}
	
}

//ADodgeDeathModifier
ADodgeDeathModifier::ADodgeDeathModifier()
{

}

bool ADodgeDeathModifier::ShouldBeRemoved() const
{
	return false;
}


void ADodgeDeathModifier::ModifierTick()
{
	m_timer += m_tickRate;
}
bool ADodgeDeathModifier::OnDamage(int32& damage, ANetworkCharacter* damageDealer)
{
	if (damage >= target->GetHealth() && m_timer > ScaleGraphCurve(cooldown))
	{
		float chance = (float)(rand() % 100) / 100.0f;
		if (ScaleGraphCurve(dodgeChance) > chance)
		{
			target->AddHealth(ScaleGraphCurve(heal));
			m_timer = 0;
			return false;
		}
	}
	return true;
}

//ACooldownReductionModifier
ACooldownReductionModifier::ACooldownReductionModifier()
{
}

void ACooldownReductionModifier::ModifyCharacter()
{
	float add = ScaleGraphCurve(scalingGraph);
	m_AddCooldownReduction(add);
}

//AAttackSpeedModifier
AAttackSpeedModifier::AAttackSpeedModifier()
{

}

void AAttackSpeedModifier::ModifyCharacter()
{
	float add = ScaleGraphCurve(scalingGraph);
	m_AddAttackSpeedMultiplier(add);
}

//AAttackSpeedModifierConstant
AAttackSpeedModifierConstant::AAttackSpeedModifierConstant()
{

}

void AAttackSpeedModifierConstant::ModifyCharacter()
{
	m_AddAttackSpeedMultiplier(attackSpeedMultiplier);
}

bool AAttackSpeedModifierConstant::ShouldBeRemoved() const
{
	return false;
}

//AArmorIgnoreModifier
AArmorIgnoreModifier::AArmorIgnoreModifier()
{

}

void AArmorIgnoreModifier::ModifyCharacter()
{
	float add = ScaleGraphCurve(scalingGraph);
	m_AddIgnoreArmor(add);
}


//ADodgeDeathModifier
ABeserkModifier::ABeserkModifier()
{
	ticksPerSecond = standardTicksPerSecond;
	m_active = false;
}

bool ABeserkModifier::ShouldBeRemoved() const
{
		return false;
}

void ABeserkModifier::AfterDamage(int32 damage)
{
	float deltahealth = (float)target->GetHealth() / (float)target->GetMaxHealth();

	if (deltahealth < ScaleGraphCurve(healthThreshold))
	{
		if (!m_active)
		{
			m_hasToActivate = true;
		}
	}
}



void ABeserkModifier::ModifierTick()
{
	float deltahealth = (float)target->GetHealth() / (float)target->GetMaxHealth();
	if (m_hasToActivate)
	{
		m_hasToActivate = false;
		m_active = true;
		m_ASModifier = GetWorld()->SpawnActor<AAttackSpeedModifierConstant>();
		m_ASModifier->lifeTime = 0.0f;
		m_ASModifier->target = target;
		m_ASModifier->attackSpeedMultiplier = ScaleGraphCurve(attackSpeedMultiplier);
		target->GetModifierManager()->AddModifier(m_ASModifier);
	}
	if (deltahealth > ScaleGraphCurve(healthThreshold))
	{
		if (m_active)
		{
			m_active = false;
			m_ASModifier->ForceDestroy();
		}
	}
}

//AArmorReductionModifier
AArmorReductionModifier::AArmorReductionModifier()
{

}


void AArmorReductionModifier::ModifyCharacter()
{
	m_AddArmor(armorReduction);
}


//AHealthRegenModifier
AHealthRegenModifier::AHealthRegenModifier()
{
	ticksPerSecond = standardTicksPerSecond;
}


void AHealthRegenModifier::ModifierTick()
{
	float add = ScaleGraphCurve(scalingGraph);
	target->AddHealth(add);
}

//AOnStandardAttackDOTModifier
AOnStandardAttackDOTModifier::AOnStandardAttackDOTModifier()
{
	ticksPerSecond = standardTicksPerSecond;
}


void AOnStandardAttackDOTModifier::ModifierTick()
{
	m_cooldownTimer += m_tickRate;
}

void AOnStandardAttackDOTModifier::OnStandardAttack(ANetworkCharacter* targetcharacter)
{
	if (!IsValid(targetcharacter))
		return;
	ADOTModifier* DOTModifier = GetWorld()->SpawnActor<ADOTModifier>();
	DOTModifier->lifeTime = ScaleGraphCurve(damageDuration);
	DOTModifier->damagePerTick = ScaleGraphCurve(damagePerTick);
//	DOTModifier->target = targetcharacter;
	DOTModifier->character = character;
	DOTModifier->abilityInfo = abilityInfo;
	DOTModifier->target = targetcharacter;
	targetcharacter->GetModifierManager()->AddModifier(DOTModifier);
	m_cooldownTimer = ScaleGraphCurve(cooldown);
}

//AReturnDamageModifier
AReturnDamageModifier::AReturnDamageModifier()
{
	ticksPerSecond = standardTicksPerSecond;

}
bool AReturnDamageModifier::OnDamage(int32& damage, ANetworkCharacter* damageDealer)
{
	if (damage >= 10 && IsValid(damageDealer))
	{

		float returnDamage = ScaleGraphCurve(scalingGraph);
		damageDealer->DealDamage(this, damage * returnDamage, 0.0f);
	}
	return true;
}


//AReturnDamageModifier
ARedirectDamageModifier::ARedirectDamageModifier()
{
	ticksPerSecond = standardTicksPerSecond;
}

bool ARedirectDamageModifier::OnDamage(int32& damage, ANetworkCharacter* damageDealer)
{
	if (damage >= 10 && IsValid(character))
	{
		float redirect = ScaleGraphCurve(redirectScalingGraph);
		if (redirect > 1)
		{
			FWARNING("redirect > 1, will act as if it is 0.9f");
			redirect = 0.9f;
		}
		if (redirect < 0)
		{
			FWARNING("redirect < 0, will act as if it is 0.0f");
			redirect = 0.0f;
		}
		float absorb = ScaleGraphCurve(absorbScalingGraph);

		if (absorb > 1)
		{
			FWARNING("absorb > 1, will act as if it is 1.0f");
			absorb = 1.0f;
		}
		if (absorb < 0.0f )
		{
			FWARNING("absorb < 0, will act as if it is 0.0f");
			absorb = 0.0f;
		}
		character->DealDamage(this, damage * (redirect * absorb), 0.0f);
		damage *= (1 - redirect);
		DamageEvent();
	}
	return true;
}


//ATrustModifier

//set delegate in gamestate to know when the teammate respawns.
ATrustModifier::ATrustModifier()
{
	ticksPerSecond = 2;
}

void ATrustModifier::ModifierTick()
{
	if (IsValid(character))
	{
		target->GetModifierManager()->RecalculateCharacter();
	}
	else
		ForceDestroy();
}

void ATrustModifier::ModifyCharacter()
{
	if (IsValid(character))
	{
		float add = ScaleGraphCurve(damageScalingGraph);
		float deltahealth =1 + ( 1.0f - ((float)character->GetHealth() / (float)character->GetMaxHealth()));
		m_MultiplyAttackDamage(add * deltahealth);
	}
}


//AArmorIncreaseModifier
AArmorIncreaseModifier::AArmorIncreaseModifier()
{
	ticksPerSecond = standardTicksPerSecond;
}

void AArmorIncreaseModifier::ModifyCharacter()
{
	float add = ScaleGraphCurve(armorScalingGraph);
	m_AddArmor(add);
}

//AMoveToModifier
AMoveToModifier::AMoveToModifier()
{
	ticksPerSecond = standardTicksPerSecond;
}
void AMoveToModifier::ModifierStart()
{
	m_movePerTick = (targetPos - target->GetActorLocation()) / (moveTime * ticksPerSecond);

}

void AMoveToModifier::ModifierTick()
{
	target->AddActorWorldOffset(m_movePerTick, true);
}

//AHealthRegenPercentageModifier
AHealthRegenPercentageModifier::AHealthRegenPercentageModifier()
{
	ticksPerSecond = standardTicksPerSecond;
}


void AHealthRegenPercentageModifier::ModifierTick()
{
	float add = ScaleGraphCurve(scalingGraph);
	add /= ticksPerSecond;
	int32 health = character->GetMaxHealth();
	health *= add;
	target->AddHealth(health);
}

//AManaRegenMultiplierModifier
AManaRegenMultiplierModifier::AManaRegenMultiplierModifier()
{
	ticksPerSecond = standardTicksPerSecond;
}

void AManaRegenMultiplierModifier::ModifyCharacter()
{
	float add = ScaleGraphCurve(manaRegenMultiplierScalingGraph);
	m_MultiplyManaRegenMultiplier(add);
}

//AManaCostMultiplierModifier
AManaCostMultiplierModifier::AManaCostMultiplierModifier()
{
	ticksPerSecond = standardTicksPerSecond;
}

void AManaCostMultiplierModifier::ModifyCharacter()
{
	float add = ScaleGraphCurve(manaCostMultiplierScalingGraph);
	m_MultiplyManaUsageMultiplier(add);
}

//AManaCostMultiplierModifier
AEffectMultiplierModifier::AEffectMultiplierModifier()
{
	ticksPerSecond = standardTicksPerSecond;
}

void AEffectMultiplierModifier::ModifyCharacter()
{
	float add = ScaleGraphCurve(effectMultiplierScalingGraph);
	if (positive)
		m_MultiplyPositiveEffectMultiplier(add);
	else
		m_MultiplyNegativeEffectMultiplier(add);
}

//ACastingMovementSpeedMultiplierModifier
ACastingMovementSpeedMultiplierModifier::ACastingMovementSpeedMultiplierModifier()
{
	ticksPerSecond = standardTicksPerSecond;
}

void ACastingMovementSpeedMultiplierModifier::ModifyCharacter()
{
	float add = ScaleGraphCurve(movementSpeedMultiplierScalingGraph);
	m_addChannelMovementSpeedMultiplier(add);
}

//AReaperModifier
AReaperModifier::AReaperModifier()
{
	ticksPerSecond = standardTicksPerSecond;
}

bool AReaperModifier::OnDealDamage(int32& damage, ANetworkCharacter* damageTaker)
{
	float multiplier = ScaleGraphCurve(damagemultiplier);
	float norm = ScaleGraphCurve(threshhold);

	float deltahealth = (float)damageTaker->GetHealth() / (float)damageTaker->GetMaxHealth();
	if ((deltahealth < norm && smaller) || (deltahealth > norm && !smaller))
	{
		damage *= multiplier;
	}

	return true;
}

//AMagicDamageMultiplierModifier
AMagicDamageMultiplierModifier::AMagicDamageMultiplierModifier()
{
	ticksPerSecond = standardTicksPerSecond;
}

void AMagicDamageMultiplierModifier::ModifyCharacter()
{
	float add = ScaleGraphCurve(magicDamageMultiplierScalingGraph);
	m_MultiplyMagicDamageMultiplier(add);
}

//AStandardMeleeModifier
AStandardMeleeModifier::AStandardMeleeModifier()
{
	count = 0;
	ticksPerSecond = standardTicksPerSecond;
}

void AStandardMeleeModifier::ModifierTick()
{
	m_timer += m_tickRate;
	if (m_timer >= maxTime)
	{
		ForceDestroy();
	}
}

bool AStandardMeleeModifier::AddCount()
{
	count++;
	count%=maxCount;
	m_timer = 0;
	return count == 0;
}

//ADamageToManaModifier
ADamageToManaModifier::ADamageToManaModifier()
{

}

void ADamageToManaModifier::AfterDamage(int32 damage)
{
	float addMultiplier = ScaleGraphCurve(multiplier);
	target->AddMana(damage * addMultiplier);
}