#include "ZoneAgProperty.h"
#include "EntityManager.h"
#include "BasePropertyServer.h"
#include "Character.h"
#include "Entity.h"
#include "GameMessages.h"
#include "dZoneManager.h"
#include "LDFFormat.h"
#include "RenderComponent.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) {
    Game::logger->Log("OnZonePropertyModelRemoved", "Removed flag updated!\n");
	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
}