// Project Lab - NHTV Igad

#include "UnrealProject.h"
#include "StatBar.h"

static UTexture2D* barTextures[6];
static FLinearColor barColors[4];
static UMaterial* textureBarMaterialClass;

UStatBar::UStatBar(const FObjectInitializer& init) : Super(init)
{
	textureBarMaterialClass = ConstructorHelpers::FObjectFinder<UMaterial>(L"/Game/Assets/Art/UI/M_HealthBar2.M_HealthBar2").Object;

	barColors[0] = FLinearColor::Green * 0.5f;
	barColors[1] = FLinearColor::Red * 0.6f;
	barColors[2] = FLinearColor::Blue * 0.8f;
	barColors[3] = FLinearColor(1.0f, 0.0f, 1.0f, 1.0f);
	barTextures[0] = ConstructorHelpers::FObjectFinder<UTexture2D>(L"/Game/Assets/Art/UI/HealthBar/UI_MainStatusBarHealth").Object;
	barTextures[1] = ConstructorHelpers::FObjectFinder<UTexture2D>(L"/Game/Assets/Art/UI/HealthBar/UI_MainStatusBarMana").Object;

	m_lastValue = 1.0f;
	m_currentValue = 1.0f;
	damageAnimationDuration = 0.3f;
	m_damageAnimation = 0.0f;
	showText = false;
}

void UStatBar::NativeConstruct()
{
	Super::NativeConstruct();
	m_base = Cast<UImage>(WidgetTree->FindWidget("Base"));
	m_blocked = Cast<UImage>(WidgetTree->FindWidget("Blocked"));
	m_damage = Cast<UImage>(WidgetTree->FindWidget("Damage"));
	m_text = Cast<UTextBlock>(WidgetTree->FindWidget("BarText"));
	if(m_damage)
	{
		m_damage->SetVisibility(ESlateVisibility::Hidden);
		UMaterialInstanceDynamic* damageMat = m_damage->GetDynamicMaterial();
		damageMat->SetScalarParameterValue("Brightness", 4.0f);
	}
	if(m_blocked)
		m_blocked->SetVisibility(ESlateVisibility::Hidden);
}

#pragma optimize("", off)
void UStatBar::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
	Super::NativeTick(MyGeometry, InDeltaTime);

	if(m_damageAnimation > 0.0f && m_damage)
	{
		float r = m_damageAnimation / damageAnimationDuration;
		float damageSize = (m_currentValue - m_lastValue);

		UMaterialInstanceDynamic* matDamage = m_damage->GetDynamicMaterial();
		if(damageSize < 0.0f)
		{
			damageSize *= (1.0f - r);
			matDamage->SetScalarParameterValue("Start", m_currentValue);
			matDamage->SetScalarParameterValue("End", m_lastValue + damageSize);
		}
		else
		{
			damageSize *= (1.0f-r);
			matDamage->SetScalarParameterValue("Start", m_lastValue + damageSize);
			matDamage->SetScalarParameterValue("End", m_currentValue);
		}

		m_damageAnimation -= InDeltaTime;
		if(m_damageAnimation <= 0.0f)
		{
			m_damageAnimation = 0.0f;
			m_damage->SetVisibility(ESlateVisibility::Hidden);
		}
	}
}

#pragma optimize("", off)
void UStatBar::SetStat(float newStat, bool applyAnimation)
{
	if(!m_base)
		return;
	if(newStat == m_currentValue)
		return;

	float delta = newStat - m_currentValue;
	if(delta != 0 && m_damageAnimation > 0.0f) 
	{
		// Append animation
		float r = m_damageAnimation / damageAnimationDuration;
		float damageSize = (m_currentValue - m_lastValue);
		if(damageSize < 0.0f)
			damageSize *= (1.0f - r);
		else
			damageSize *= (1.0f - r);
		delta += damageSize;
		m_lastValue = m_lastValue + damageSize;
	}
	else 
	{
		m_lastValue = m_currentValue;
	}
	m_currentValue = newStat;

	// Apply animation
	if(delta != 0.0f && applyAnimation && m_damage)
	{
		const FLinearColor deltaColor = FLinearColor(1.0f, 0.0f, 0.0f, 1.0f);
		m_damageAnimation = damageAnimationDuration;

		UMaterialInstanceDynamic* matDamage = m_base->GetDynamicMaterial();
		if(delta < 0.0f)
		{
			matDamage->SetScalarParameterValue("Start", newStat);
			matDamage->SetScalarParameterValue("End", m_lastValue);
		}
		else
		{
			matDamage->SetScalarParameterValue("Start", m_lastValue);
			matDamage->SetScalarParameterValue("End", newStat);
		}
		m_damage->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
	}

	UMaterialInstanceDynamic* matBase = m_base->GetDynamicMaterial();
	matBase->SetScalarParameterValue("Start", 0.0f);
	matBase->SetScalarParameterValue("End", newStat);
}

void UStatBar::SetStat(int32 newStat, int32 max, bool applyAnimation)
{
	if(showText && m_text)
	{
		FText t = FText::FromString(FString::Printf(L"%d / %d", newStat, max));
		m_text->SetText(t);
		m_text->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
	}
	else
	{
		m_text->SetVisibility(ESlateVisibility::Hidden);
	}

	float r = (float)newStat / (float)max;
	SetStat(r, applyAnimation);
}

void UStatBar::SetBlocked(float newBlocked)
{
	if(!m_blocked)
		return;
	UMaterialInstanceDynamic* mat = m_blocked->GetDynamicMaterial();
	if(newBlocked > 0.0f)
	{
		mat->SetScalarParameterValue("Start", 1.0f - newBlocked);
		mat->SetScalarParameterValue("End", 1.0f);
		m_blocked->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
	}
	else
	{
		m_blocked->SetVisibility(ESlateVisibility::Hidden);
	}
}

void UStatBar::SetBarColor(EBarColor _colorID)
{
	int32 colorID = (int32)_colorID;
	if(!m_base)
		return;

	if((colorID & 0x80) != 0)
	{
		colorID = colorID & 0x7F;
		colorID = FMath::Clamp(colorID, 0, (int32)(sizeof(barTextures) / sizeof(UTexture2D*)));

		m_base->SetBrushFromMaterial(textureBarMaterialClass);
		m_blocked->SetBrushFromMaterial(textureBarMaterialClass);
		m_damage->SetBrushFromMaterial(textureBarMaterialClass);
		UMaterialInstanceDynamic* baseMat = m_base->GetDynamicMaterial();
		baseMat->SetTextureParameterValue("Texture", barTextures[colorID]);
		baseMat = m_damage->GetDynamicMaterial();
		baseMat->SetTextureParameterValue("Texture", barTextures[colorID]);
		baseMat = m_blocked->GetDynamicMaterial();
		baseMat->SetTextureParameterValue("Texture", barTextures[colorID]);
	}
	else
	{
		colorID = FMath::Clamp(colorID, 0, (int32)(sizeof(barColors) / sizeof(FLinearColor)));

		UMaterialInstanceDynamic* baseMat = m_base->GetDynamicMaterial();
		baseMat->SetVectorParameterValue("Color", barColors[colorID]);
	}
}
