#include "ZoneAgProperty.h"
#include "EntityManager.h"
#include "Character.h"
#include "Entity.h"
#include "GameMessages.h"
#include "dZoneManager.h"
#include "RenderComponent.h"
#include "MissionComponent.h"

void ZoneAgProperty::SetGameVariables(Entity* self) {
	self->SetVar<std::string>(GuardGroup, "Guard");
	self->SetVar<std::string>(PropertyPlaqueGroup, "PropertyPlaque");
	self->SetVar<std::string>(PropertyVendorGroup, "PropertyVendor");
	self->SetVar<std::string>(PropertyBorderGroup, "PropertyBorder");
	self->SetVar<std::string>(LandTargetGroup, "Land_Target");
	self->SetVar<std::string>(SpiderScreamGroup, "Spider_Scream");
	self->SetVar<std::vector<std::string>>(ROFTargetsGroup, { "ROF_Targets_00", "ROF_Targets_01", "ROF_Targets_02", "ROF_Targets_03", "ROF_Targets_04" });
	self->SetVar<std::string>(SpiderEggsGroup, "SpiderEggs");
	self->SetVar<std::string>(RocksGroup, "Rocks");
	self->SetVar<std::string>(EnemiesGroup, "SpiderBoss");
	self->SetVar<std::vector<std::string>>(ZoneVolumesGroup, { "Zone1Vol", "Zone2Vol", "Zone3Vol", "Zone4Vol", "Zone5Vol", "Zone6Vol", "Zone7Vol", "Zone8Vol", "AggroVol", "TeleVol" });
	self->SetVar<std::string>(FXManagerGroup, "FXObject");

	self->SetVar<std::string>(EnemiesSpawner, "SpiderBoss");
	self->SetVar<std::vector<std::string>>(BossSensorSpawner, { "Zone1Vol", "Zone2Vol", "Zone3Vol", "Zone4Vol", "Zone5Vol", "Zone6Vol", "Zone7Vol", "Zone8Vol", "RFS_Targets", "AggroVol", "TeleVol" });
	self->SetVar<std::string>(LandTargetSpawner, "Land_Target");
	self->SetVar<std::string>(SpiderScreamSpawner, "Spider_Scream");
	self->SetVar<std::vector<std::string>>(ROFTargetsSpawner, { "ROF_Targets_00", "ROF_Targets_01", "ROF_Targets_02", "ROF_Targets_03", "ROF_Targets_04" });
	self->SetVar<std::string>(PropertyMGSpawner, "PropertyGuard");
	self->SetVar<std::string>(FXManagerSpawner, "FXObject");
	self->SetVar<std::string>(PropObjsSpawner, "BankObj");
	self->SetVar<std::string>(SpiderEggsSpawner, "SpiderEggs");
	self->SetVar<std::string>(RocksSpawner, "Rocks");
	self->SetVar<std::vector<std::string>>(AmbientFXSpawner, { "BirdFX", "SunBeam" });
	self->SetVar<std::vector<std::string>>(SpiderRocketSpawner, { "SpiderRocket_Bot", "SpiderRocket_Mid", "SpiderRocket_Top" });
	self->SetVar<std::string>(MailboxSpawner, "Mailbox");
	self->SetVar<std::string>(LauncherSpawner, "Launcher");
	self->SetVar<std::string>(InstancerSpawner, "Instancer");

	self->SetVar<uint32_t>(defeatedProperyFlag, 71);
	self->SetVar<uint32_t>(placedModelFlag, 73);
	self->SetVar<uint32_t>(guardFirstMissionFlag, 891);
	self->SetVar<uint32_t>(guardMissionFlag, 320);
	self->SetVar<uint32_t>(brickLinkMissionIDFlag, 951);
}

void ZoneAgProperty::OnStartup(Entity* self) {
	LoadProperty(self);
}

void ZoneAgProperty::OnPlayerLoaded(Entity* self, Entity* player) {
	CheckForOwner(self);

	auto rented = self->GetVar<LWOOBJID>(u"PropertyOwner") == LWOOBJID_EMPTY;
	self->SetVar<bool>(u"rented", rented);

	if (!rented) {
		const auto numberOfPlayers = self->GetVar<int32_t>(u"numberOfPlayers");
		self->SetVar<int32_t>(u"numberOfPlayers", numberOfPlayers + 1);
	}

	if (dZoneManager::Instance()->GetZone()->GetZoneID().GetMapID() == 1102) {
		GameMessages::SendPlay2DAmbientSound(player, GUIDMaelstrom);
		GameMessages::SendNotifyClientObject(self->GetObjectID(), u"maelstromSkyOn", 0, 0,
			LWOOBJID_EMPTY, "", player->GetSystemAddress());

		self->SetNetworkVar(u"unclaimed", true);

		return;
	}

	BasePlayerLoaded(self, player);
}

void ZoneAgProperty::PropGuardCheck(Entity* self, Entity* player) {
	auto* missionComponent = player->GetComponent<MissionComponent>();
	if (missionComponent == nullptr)
		return;

	const auto state = missionComponent->GetMissionState(self->GetVar<uint32_t>(guardMissionFlag));
	const auto firstState = missionComponent->GetMissionState(self->GetVar<uint32_t>(guardFirstMissionFlag));

	if (firstState < MissionState::MISSION_STATE_COMPLETE || (state != MissionState::MISSION_STATE_COMPLETE && state != MissionState::MISSION_STATE_COMPLETE_READY_TO_COMPLETE))
		ActivateSpawner(self->GetVar<std::string>(PropertyMGSpawner));
}

void ZoneAgProperty::OnZoneLoadedInfo(Entity* self) {
	LoadProperty(self);
}

void ZoneAgProperty::LoadInstance(Entity* self) {
	SetGameVariables(self);

	for (auto* spawner : dZoneManager::Instance()->GetSpawnersByName(self->GetVar<std::string>(InstancerSpawner))) {
		for (auto* spawnerNode : spawner->m_Info.nodes) {
			spawnerNode->config.push_back(
				new LDFData<std::string>(u"custom_script_server",
					R"(scripts\ai\GENERAL\L_INSTANCE_EXIT_TRANSFER_PLAYER_TO_LAST_NON_INSTANCE.lua)"));
			spawnerNode->config.push_back(new LDFData<std::u16string>(u"transferText", u"SPIDER_QUEEN_EXIT_QUESTION"));
		}
	}

	ActivateSpawner(self->GetVar<std::string>(InstancerSpawner));
}

void ZoneAgProperty::LoadProperty(Entity* self) {
	SetGameVariables(self);
	ActivateSpawner(self->GetVar<std::string>(LauncherSpawner));
	ActivateSpawner(self->GetVar<std::string>(MailboxSpawner));
}

void ZoneAgProperty::ProcessGroupObjects(Entity* self, std::string group) {
}

void ZoneAgProperty::SpawnSpots(Entity* self) {
	for (const auto& spot : self->GetVar<std::vector<std::string>>(ROFTargetsSpawner)) {
		ActivateSpawner(spot);
	}

	ActivateSpawner(self->GetVar<std::string>(LandTargetSpawner));
}

void ZoneAgProperty::KillSpots(Entity* self) {
	for (const auto& spot : self->GetVar<std::vector<std::string>>(ROFTargetsSpawner)) {
		DeactivateSpawner(spot);
	}

	for (const auto& groupName : self->GetVar<std::vector<std::string>>(ROFTargetsGroup)) {
		for (auto* spot : EntityManager::Instance()->GetEntitiesInGroup(groupName)) {
			spot->Kill();
		}
	}

	DeactivateSpawner(self->GetVar<std::string>(LandTargetSpawner));
	for (auto* landTarget : EntityManager::Instance()->GetEntitiesInGroup(self->GetVar<std::string>(LandTargetSpawner))) {
		landTarget->Kill();
	}
}

void ZoneAgProperty::SpawnCrashedRocket(Entity* self) {
	for (const auto& rocket : self->GetVar<std::vector<std::string>>(SpiderRocketSpawner)) {
		ActivateSpawner(rocket);
	}
}

void ZoneAgProperty::KillCrashedRocket(Entity* self) {
	for (const auto& rocket : self->GetVar<std::vector<std::string>>(SpiderRocketSpawner)) {
		DeactivateSpawner(rocket);
		DestroySpawner(rocket);
	}
}

void ZoneAgProperty::StartMaelstrom(Entity* self, Entity* player) {
	ActivateSpawner(self->GetVar<std::string>(EnemiesSpawner));
	for (const auto& sensor : self->GetVar<std::vector<std::string>>(BossSensorSpawner)) {
		ActivateSpawner(sensor);
	}

	ActivateSpawner(self->GetVar<std::string>(FXManagerSpawner));
	ActivateSpawner(self->GetVar<std::string>(SpiderScreamSpawner));
	ActivateSpawner(self->GetVar<std::string>(SpiderEggsSpawner));
	ActivateSpawner(self->GetVar<std::string>(RocksSpawner));

	SpawnCrashedRocket(self);

	for (const auto& ambient : self->GetVar<std::vector<std::string>>(AmbientFXSpawner)) {
		DeactivateSpawner(ambient);
		DestroySpawner(ambient);
		ResetSpawner(ambient);
	}

	StartTornadoFx(self);

	if (player != nullptr) {
		GameMessages::SendNotifyClientObject(self->GetObjectID(), u"maelstromSkyOn", 0, 0, LWOOBJID_EMPTY,
			"", player->GetSystemAddress());
	}
}

uint32_t ZoneAgProperty::RetrieveSpawnerId(Entity* self, const std::string& spawner) {
	auto spawnerIDs = dZoneManager::Instance()->GetSpawnersByName(spawner);
	if (spawnerIDs.empty())
		return 0;

	return spawnerIDs[0]->m_Info.spawnerID;
}

void ZoneAgProperty::OnTimerDone(Entity* self, std::string timerName) {
	BaseTimerDone(self, timerName);
}

void ZoneAgProperty::BaseTimerDone(Entity* self, const std::string& timerName) {
	if (timerName == "GuardFlyAway") {
		const auto zoneId = dZoneManager::Instance()->GetZone()->GetWorldID();
		if (zoneId != 1150)
			return;

		const auto entities = EntityManager::Instance()->GetEntitiesInGroup(self->GetVar<std::string>(GuardGroup));
		if (entities.empty())
			return;

		auto* entity = entities[0];

		GameMessages::SendNotifyClientObject(EntityManager::Instance()->GetZoneControlEntity()->GetObjectID(), u"GuardChat", 0, 0, entity->GetObjectID(), "", UNASSIGNED_SYSTEM_ADDRESS);
		LoadProperty(self);

		self->AddTimer("KillGuard", 5);
	} else if (timerName == "KillGuard") {
		KillGuard(self);
	} else if (timerName == "tornadoOff") {
		for (auto* entity : EntityManager::Instance()->GetEntitiesInGroup(self->GetVar<std::string>(FXManagerGroup))) {
			auto* renderComponent = entity->GetComponent<RenderComponent>();
			if (renderComponent != nullptr) {
				renderComponent->StopEffect("TornadoDebris", false);
				renderComponent->StopEffect("TornadoVortex", false);
				renderComponent->StopEffect("silhouette", false);
			}
		}

		self->AddTimer("ShowVendor", 1.2f);
		self->AddTimer("ShowClearEffects", 2);
	} else if (timerName == "ShowClearEffects") {
		for (auto* entity : EntityManager::Instance()->GetEntitiesInGroup(self->GetVar<std::string>(FXManagerGroup))) {
			auto* renderComponent = entity->GetComponent<RenderComponent>();
			if (renderComponent != nullptr) {
				renderComponent->PlayEffect(-1, u"beamOn", "beam");
			}
		}

		self->AddTimer("killSpider", 2);
		self->AddTimer("turnSkyOff", 1.5f);
		self->AddTimer("killFXObject", 8);
	} else if (timerName == "turnSkyOff") {
		GameMessages::SendNotifyClientObject(self->GetObjectID(), u"SkyOff", 0, 0, LWOOBJID_EMPTY,
			"", UNASSIGNED_SYSTEM_ADDRESS);
	} else if (timerName == "killSpider") {
		for (auto* entity : EntityManager::Instance()->GetEntitiesInGroup(self->GetVar<std::string>(EnemiesGroup))) {
			entity->Kill();
		}

		for (const auto& sensor : self->GetVar<std::vector<std::string>>(BossSensorSpawner)) {
			DeactivateSpawner(sensor);
			DestroySpawner(sensor);
		}

		DeactivateSpawner(self->GetVar<std::string>(SpiderEggsSpawner));
		DestroySpawner(self->GetVar<std::string>(SpiderEggsSpawner));

		DeactivateSpawner(self->GetVar<std::string>(RocksSpawner));
		DestroySpawner(self->GetVar<std::string>(RocksSpawner));

		KillSpots(self);
		KillCrashedRocket(self);

		DeactivateSpawner(self->GetVar<std::string>(SpiderScreamSpawner));
		DestroySpawner(self->GetVar<std::string>(SpiderScreamSpawner));

		for (auto* player : EntityManager::Instance()->GetEntitiesByComponent(COMPONENT_TYPE_CHARACTER)) {
			GameMessages::SendStop2DAmbientSound(player, true, GUIDMaelstrom);
			GameMessages::SendPlay2DAmbientSound(player, GUIDPeaceful);
		}
	} else if (timerName == "ShowVendor") {
		GameMessages::SendNotifyClientObject(self->GetObjectID(), u"vendorOn", 0, 0, LWOOBJID_EMPTY, "", UNASSIGNED_SYSTEM_ADDRESS);
		for (const auto& ambient : self->GetVar<std::vector<std::string>>(AmbientFXSpawner)) {
			ActivateSpawner(ambient);
		}
	} else if (timerName == "BoundsVisOn") {
		GameMessages::SendNotifyClientObject(self->GetObjectID(), u"boundsAnim", 0, 0,
			LWOOBJID_EMPTY, "", UNASSIGNED_SYSTEM_ADDRESS);
	} else if (timerName == "runPlayerLoadedAgain") {
		CheckForOwner(self);
	} else if (timerName == "pollTornadoFX") {
		StartTornadoFx(self);
	} else if (timerName == "killFXObject") {
		for (auto* entity : EntityManager::Instance()->GetEntitiesInGroup(self->GetVar<std::string>(FXManagerGroup))) {
			auto* renderComponent = entity->GetComponent<RenderComponent>();
			if (renderComponent != nullptr) {
				renderComponent->StopEffect("beam");
			}
		}

		DestroySpawner(self->GetVar<std::string>(FXManagerSpawner));

		self->SetVar<bool>(u"FXObjectGone", true);
	} else if (timerName == "ProcessGroupObj") {
		// TODO
	}
}

void ZoneAgProperty::OnZonePropertyRented(Entity* self, Entity* player) {
	BaseZonePropertyRented(self, player);

	auto* character = player->GetCharacter();
	if (character == nullptr)
		return;

	character->SetPlayerFlag(108, true);
}

void ZoneAgProperty::OnZonePropertyModelPlaced(Entity* self, Entity* player) {
	auto* character = player->GetCharacter();
	auto* missionComponent = player->GetComponent<MissionComponent>();

	if (!character->GetPlayerFlag(101)) {
		BaseZonePropertyModelPlaced(self, player);
		character->SetPlayerFlag(101, true);
		if (missionComponent->GetMissionState(871) == MissionState::MISSION_STATE_ACTIVE) {
			self->SetNetworkVar<std::u16string>(u"Tooltip", u"AnotherModel");
		}

	} else if (!character->GetPlayerFlag(102)) {
		character->SetPlayerFlag(102, true);
		if (missionComponent->GetMissionState(871) == MissionState::MISSION_STATE_ACTIVE) {
			self->SetNetworkVar<std::u16string>(u"Tooltip", u"TwoMoreModels");
		}

	} else if (!character->GetPlayerFlag(103)) {
		character->SetPlayerFlag(103, true);
	} else if (!character->GetPlayerFlag(104)) {
		character->SetPlayerFlag(104, true);
		self->SetNetworkVar<std::u16string>(u"Tooltip", u"TwoMoreModelsOff");
	} else if (self->GetVar<std::string>(u"tutorial") == "place_model") {
		self->SetVar<std::string>(u"tutorial", "");
		self->SetNetworkVar<std::u16string>(u"Tooltip", u"PutAway");
	}
}

void ZoneAgProperty::OnZonePropertyModelPickedUp(Entity* self, Entity* player) {
	auto* character = player->GetCharacter();
	auto* missionComponent = player->GetComponent<MissionComponent>();

	if (!character->GetPlayerFlag(109)) {
		character->SetPlayerFlag(109, true);
		if (missionComponent->GetMissionState(891) == MissionState::MISSION_STATE_ACTIVE && !character->GetPlayerFlag(110)) {
			self->SetNetworkVar<std::u16string>(u"Tooltip", u"Rotate");
		}
	}
}

void ZoneAgProperty::OnZonePropertyModelRemoved(Entity* self, Entity* player) {
	auto* character = player->GetCharacter();
	character->SetPlayerFlag(111, true);
}

void ZoneAgProperty::OnZonePropertyModelRemovedWhileEquipped(Entity* self, Entity* player) {
	ZoneAgProperty::OnZonePropertyModelRemoved(self, player);
}

void ZoneAgProperty::OnZonePropertyModelRotated(Entity* self, Entity* player) {
	auto* character = player->GetCharacter();
	auto* missionComponent = player->GetComponent<MissionComponent>();

	if (!character->GetPlayerFlag(110)) {
		character->SetPlayerFlag(110, true);

		if (missionComponent->GetMissionState(891) == MissionState::MISSION_STATE_ACTIVE) {
			self->SetNetworkVar<std::u16string>(u"Tooltip", u"PlaceModel");
			self->SetVar<std::string>(u"tutorial", "place_model");
		}
	}
}

void ZoneAgProperty::OnZonePropertyModelEquipped(Entity* self) {
	self->SetNetworkVar<std::u16string>(u"PlayerAction", u"ModelEquipped");
}

void ZoneAgProperty::OnZonePropertyEditBegin(Entity* self) {
	self->SetNetworkVar<std::u16string>(u"PlayerAction", u"Enter");
}

void ZoneAgProperty::OnZonePropertyEditEnd(Entity* self) {
	self->SetNetworkVar<std::u16string>(u"PlayerAction", u"Exit");
}

void ZoneAgProperty::OnPlayerExit(Entity* self) {
	// TODO: Destroy stuff
}

void ZoneAgProperty::RemovePlayerRef(Entity* self) {
	// TODO: Destroy stuff
}

void ZoneAgProperty::BaseOnFireEventServerSide(Entity* self, Entity* sender, std::string args) {
	if (args == "propertyRented") {
		const auto playerId = self->GetVar<LWOOBJID>(u"playerID");
		auto* player = EntityManager::Instance()->GetEntity(playerId);
		if (player == nullptr)
			return;

		OnZonePropertyRented(self, player);
	} else if (args == "RetrieveZoneData") {
		self->SetVar<LWOOBJID>(u"SpiderBossID", sender->GetObjectID());
		sender->SetVar<int32_t>(u"SpiderEggNetworkID", RetrieveSpawnerId(self, self->GetVar<std::string>(SpiderEggsSpawner)));

		std::vector<uint32_t> table;

		for (const auto& target : self->GetVar<std::vector<std::string>>(ROFTargetsSpawner)) {
			table.push_back(RetrieveSpawnerId(self, target));
		}

		ROFTargetGroupIdTable = table;

		ProcessGroupObjects(self, self->GetVar<std::string>(LandTargetGroup));
		ProcessGroupObjects(self, self->GetVar<std::string>(SpiderScreamGroup));
		//        ProcessGroupObjects(self, groups.ZoneVolumes);
	} else if (args == "CheckForPropertyOwner") {
		sender->SetNetworkVar<std::string>(u"PropertyOwnerID", std::to_string(self->GetVar<LWOOBJID>(u"PropertyOwner")));
	} else if (args == "ClearProperty") {
		const auto playerId = self->GetVar<LWOOBJID>(u"playerID");
		auto* player = EntityManager::Instance()->GetEntity(playerId);
		if (player == nullptr)
			return;

		player->GetCharacter()->SetPlayerFlag(self->GetVar<uint32_t>(defeatedProperyFlag), true);
		GameMessages::SendNotifyClientObject(self->GetObjectID(), u"PlayCinematic", 0, 0,
			LWOOBJID_EMPTY, destroyedCinematic, UNASSIGNED_SYSTEM_ADDRESS);

		self->AddTimer("tornadoOff", 0.5f);
	}
}

void ZoneAgProperty::NotifyDie(Entity* self) {
	// TODO
}