#include "DamagingPets.h"
#include "PetComponent.h"
#include "DestroyableComponent.h"
#include "BaseCombatAIComponent.h"
#include "RenderComponent.h"

void DamagingPets::OnStartup(Entity* self) {

	// Make the pet hostile or non-hostile based on whether or not it is tamed
	const auto* petComponent = self->GetComponent<PetComponent>();
	if (petComponent != nullptr && petComponent->GetOwner() == nullptr) {
		self->AddTimer("GoEvil", 0.5f);
	}
}

void DamagingPets::OnPlayerLoaded(Entity* self, Entity* player) {

	// Makes it so that new players also see the effect
	self->AddCallbackTimer(2.5f, [self]() {
		if (self != nullptr) {
			const auto* petComponent = self->GetComponent<PetComponent>();
			if (petComponent != nullptr && petComponent->GetOwner() == nullptr && self->GetVar<bool>(u"IsEvil")) {
				auto* renderComponent = self->GetComponent<RenderComponent>();
				if (renderComponent != nullptr) {
					auto counter = 1;
					for (const auto petEffect : GetPetInfo(self).effect) {
						renderComponent->PlayEffect(petEffect, u"create", "FXname" + std::to_string(counter));
						counter++;
					}
				}
			}
		}
		});
}

void DamagingPets::OnNotifyPetTamingMinigame(Entity* self, Entity* tamer, eNotifyType type) {
	switch (type) {
	case NOTIFY_TYPE_SUCCESS:
	case NOTIFY_TYPE_BEGIN:
		self->CancelAllTimers();
		ClearEffects(self);
		break;
	case NOTIFY_TYPE_FAILED:
	case NOTIFY_TYPE_QUIT:
	{
		self->SetNetworkVar<bool>(u"bIAmTamable", false);
		self->AddTimer("GoEvil", 1.0f);
		break;
	}
	default:
		break;
	}
}

void DamagingPets::OnSkillEventFired(Entity* self, Entity* caster, const std::string& message) {
	const auto infoForPet = GetPetInfo(self);
	if (infoForPet.skill == message) {

		// Only make pets tamable that aren't tamed yet
		const auto* petComponent = self->GetComponent<PetComponent>();
		if (petComponent != nullptr && petComponent->GetOwner() == nullptr && self->GetVar<bool>(u"IsEvil")) {
			ClearEffects(self);
			self->AddTimer("GoEvil", 30.0f);
			self->SetNetworkVar<bool>(u"bIAmTamable", true);
		}
	}
}

void DamagingPets::OnTimerDone(Entity* self, std::string message) {
	if (message == "GoEvil") {
		MakeUntamable(self);
	}
}

void DamagingPets::MakeUntamable(Entity* self) {
	auto* petComponent = self->GetComponent<PetComponent>();

	// If the pet is currently not being tamed, make it hostile
	if (petComponent != nullptr && petComponent->GetStatus() != 5) {
		self->SetNetworkVar<bool>(u"bIAmTamable", false);
		self->SetVar<bool>(u"IsEvil", true);
		petComponent->SetStatus(1);

		auto* combatAIComponent = self->GetComponent<BaseCombatAIComponent>();
		if (combatAIComponent != nullptr) {
			combatAIComponent->SetDisabled(false);
		}

		// Special faction that can attack the player but the player can't attack
		auto* destroyableComponent = self->GetComponent<DestroyableComponent>();
		if (destroyableComponent != nullptr) {
			destroyableComponent->SetFaction(114);
			destroyableComponent->SetHealth(5);
		}

		auto* renderComponent = self->GetComponent<RenderComponent>();
		if (renderComponent != nullptr) {
			auto counter = 1;
			for (const auto petEffect : GetPetInfo(self).effect) {
				renderComponent->PlayEffect(petEffect, u"create", "FXname" + std::to_string(counter));
				counter++;
			}
		}
	}
}

void DamagingPets::ClearEffects(Entity* self) {
	self->SetVar<bool>(u"IsEvil", false);

	auto* petComponent = self->GetComponent<PetComponent>();
	if (petComponent != nullptr) {
		petComponent->SetStatus(67108866);
	}

	auto* combatAIComponent = self->GetComponent<BaseCombatAIComponent>();
	if (combatAIComponent != nullptr) {
		combatAIComponent->SetDisabled(true);
	}

	auto* destroyableComponent = self->GetComponent<DestroyableComponent>();
	if (destroyableComponent != nullptr) {
		destroyableComponent->SetFaction(99);
	}

	auto* renderComponent = self->GetComponent<RenderComponent>();
	if (renderComponent != nullptr) {
		auto counter = 1;
		for (const auto petEffect : GetPetInfo(self).effect) {
			renderComponent->StopEffect("FXname" + std::to_string(counter));
			counter++;
		}
	}
}

PetInfo DamagingPets::GetPetInfo(Entity* self) {
	const auto infoForPet = petInfo.find(self->GetLOT());
	return infoForPet != petInfo.end() ? infoForPet->second : petInfo.begin()->second;
}

// Does not compile on Win32 with name specifiers
const std::map<LOT, PetInfo> DamagingPets::petInfo = {
		{ 5639, { /*.effect =*/ { 3170, 4058 }, /*.skill =*/ "waterspray"}}, // Red dragon
		{ 5641, { /*.effect =*/ { 3170, 4058 }, /*.skill =*/ "waterspray"}}, // Green dragon
		{ 3261, { /*.effect =*/ { 1490 }, /*.skill =*/ "waterspray"}}, // Skunk
};