/**
 * Canary - A free and open-source MMORPG server emulator
 * Copyright (©) 2019–present OpenTibiaBR <opentibiabr@outlook.com>
 * Repository: https://github.com/opentibiabr/canary
 * License: https://github.com/opentibiabr/canary/blob/main/LICENSE
 * Contributors: https://github.com/opentibiabr/canary/graphs/contributors
 * Website: https://docs.opentibiabr.com/
 */

#include "map/utils/mapsector.hpp"

#include "creatures/creature.hpp"

#ifndef USE_PRECOMPILED_HEADERS
	#include <limits>
#endif

bool MapSector::newSector = false;

MapSector::MapSector() {
	for (auto &revision : topologyRevisions) {
		revision.store(1, std::memory_order_relaxed);
	}
	for (auto &revision : occupancyRevisions) {
		revision.store(1, std::memory_order_relaxed);
	}
}

namespace {
	uint64_t incrementRevision(std::atomic_uint64_t &revision) {
		auto current = revision.load(std::memory_order_relaxed);
		while (true) {
			const auto next = current == std::numeric_limits<uint64_t>::max() ? 1 : current + 1;
			if (revision.compare_exchange_weak(current, next, std::memory_order_relaxed)) {
				return next;
			}
		}
	}
}

uint64_t MapSector::markTopologyChanged(uint8_t z) {
	return z < MAP_MAX_LAYERS ? incrementRevision(topologyRevisions[z]) : 0;
}

uint64_t MapSector::markOccupancyChanged(uint8_t z) {
	return z < MAP_MAX_LAYERS ? incrementRevision(occupancyRevisions[z]) : 0;
}

void MapSector::addCreature(const std::shared_ptr<Creature> &c) {
	creature_list.emplace_back(c);
	if (c->getPlayerRaw()) {
		player_list.emplace_back(c);
	} else if (c->getMonsterRaw()) {
		monster_list.emplace_back(c);
	} else if (c->getNpcRaw()) {
		npc_list.emplace_back(c);
	}
}

void MapSector::removeCreature(const std::shared_ptr<Creature> &c) {
	auto iter = std::ranges::find(creature_list, c);
	if (iter == creature_list.end()) {
		g_logger().error("[{}]: Creature not found in creature_list!", __FUNCTION__);
		return;
	}

	assert(iter != creature_list.end());
	*iter = creature_list.back();
	creature_list.pop_back();

	if (c->getPlayerRaw()) {
		iter = std::ranges::find(player_list, c);
		if (iter == player_list.end()) {
			g_logger().error("[{}]: Player not found in player_list!", __FUNCTION__);
			return;
		}

		assert(iter != player_list.end());
		*iter = player_list.back();
		player_list.pop_back();
	} else if (c->getMonsterRaw()) {
		iter = std::ranges::find(monster_list, c);
		if (iter == monster_list.end()) {
			g_logger().error("[{}]: Monster not found in monster_list!", __FUNCTION__);
			return;
		}

		assert(iter != monster_list.end());
		*iter = monster_list.back();
		monster_list.pop_back();
	} else if (c->getNpcRaw()) {
		iter = std::ranges::find(npc_list, c);
		if (iter == npc_list.end()) {
			g_logger().error("[{}]: NPC not found in npc_list!", __FUNCTION__);
			return;
		}

		assert(iter != npc_list.end());
		*iter = npc_list.back();
		npc_list.pop_back();
	}
}
