#include "server/game/ServerWorld.h"
#include "server/Server.h"
#include "td/misc/Random.h"

#include "td/protocol/packets/SpawnMobPacket.h"
#include "td/protocol/packets/UpdateCastleLifePacket.h"

#define MOB_SPAWN_PRECISION 100.0f

namespace td {
namespace server {

ServerWorld::ServerWorld(Server* server, ServerGame* game) : game::World(game), m_CurrentMobID(0), m_Server(server) {

}

void ServerWorld::SpawnMobs(game::MobType type, std::uint8_t level, game::PlayerID sender, std::uint8_t count) {
	for (int i = 0; i < count; i++) {
		game::TeamColor senderTeam = m_Game->GetPlayers().at(sender).GetTeamColor();
		game::Spawn* enemyMobSpawn;

		if (senderTeam == game::TeamColor::Red) {
			enemyMobSpawn = &GetTeam(game::TeamColor::Blue).GetSpawn();
		} else {
			enemyMobSpawn = &GetTeam(game::TeamColor::Red).GetSpawn();
		}

		float spawnWidth = enemyMobSpawn->GetWidth();
		float spawnHeight = enemyMobSpawn->GetHeight();

		float spawnCenterX = enemyMobSpawn->GetCenterX();
		float spawnCenterY = enemyMobSpawn->GetCenterY();

		auto mobStats = GetMobStats(type, level);
		auto mobSize = mobStats->GetSize();

		float minSpawnX = spawnCenterX - spawnWidth / 2.0f + mobSize.x / 2.0f;
		float maxSpawnX = spawnCenterX + spawnWidth / 2.0f - mobSize.x / 2.0f;

		float minSpawnY = spawnCenterY - spawnHeight / 2.0f + mobSize.y / 2.0f;
		float maxSpawnY = spawnCenterY + spawnHeight / 2.0f - mobSize.y / 2.0f;

		float mobX = utils::GetRandomReal<float>(minSpawnX + MobSpawnBorder, maxSpawnX - MobSpawnBorder);
		float mobY = utils::GetRandomReal<float>(minSpawnY + MobSpawnBorder, maxSpawnY - MobSpawnBorder);

		SpawnMobAt(m_CurrentMobID, type, level, sender, mobX, mobY, enemyMobSpawn->GetDirection());

		protocol::SpawnMobPacket packet(m_CurrentMobID, type, level, sender, mobX, mobY, enemyMobSpawn->GetDirection());
		m_Server->BroadcastPacket(&packet);

		m_CurrentMobID++;
	}
}

game::TowerPtr ServerWorld::PlaceTowerAt(game::TowerType type, std::int32_t x, std::int32_t y, game::PlayerID builder) {
	game::TowerPtr tower = World::PlaceTowerAt(m_CurrentTowerID, type, x, y, builder);
	m_CurrentTowerID++;
	return tower;
}

void ServerWorld::OnMobDie(game::Mob* mob) {
	if (mob->OnDeath(this)) { // check if the mob is actually dead (slimes ...)
		//reward players
		game::Player* sender = m_Game->GetPlayerById(mob->GetSender());
		sender->AddExp(mob->GetStats()->GetExpReward());

		game::Player* killer = m_Game->GetPlayerById(mob->GetLastDamageTower()->GetBuilder());
		killer->AddGold(mob->GetStats()->GetMoneyCost());
	}
}

void ServerWorld::OnMobCastleDamage(game::Mob* damager, game::TeamCastle* enemyCastle, float damage) {
	// calling base class event
	World::OnMobCastleDamage(damager, enemyCastle, damage);

	protocol::UpdateCastleLifePacket packet(enemyCastle->GetLife(), enemyCastle->GetTeam()->GetColor());
	m_Server->BroadcastPacket(&packet);
}

} // namespace server
} // namespace td
