#pragma once

/**
 * \file Server.h
 * \brief File containing the blitz::server::Server class
 */

#include <map>
#include <thread>

#include "blitz/misc/TickCounter.h"
#include "blitz/network/TCPListener.h"
#include "server/ServerConnexion.h"
#include "server/game/ServerGame.h"

/**
 * \def SERVER_TPS
 * \brief The server ticks per second
 */
#define SERVER_TPS 20
/**
 * \def SERVER_TICK
 * \brief The server tick duration
 */
#define SERVER_TICK 1000 / SERVER_TPS

namespace blitz {
namespace server {

typedef std::map<std::uint8_t, std::unique_ptr<ServerConnexion>> ConnexionMap;

/**
 * \class Server
 * \brief Class used to manage the server
 */
class Server {
  private:
	network::TCPListener m_Listener;
	ConnexionMap m_Connections;
	utils::TickCounter m_TickCounter;

	ServerGame m_Game;

	std::thread m_Thread;
	bool m_ServerRunning;

  public:
	/**
	 * \brief Constructor
	 */
	Server();
	virtual ~Server();

	/**
	 * \brief Start the server
	 * \param port The port
	 * \param blocking If the server is blocking
	 * \return If the server has started
	 */
	bool Start(std::uint16_t port, bool blocking);
	/**
	 * \brief Force the server to stop
	 */
	void Stop();

	/**
	 * \brief Restart the server
	 */
	void Restart();

	/**
	 * \brief Add a bot
	 */
	void AddBot();

	/**
	 * \brief Forcefully remove a player from the game
	 * \param player the id of the player to remove
	*/
	void KickPlayer(game::PlayerID player);

	/**
	 * \brief Remove a connexion
	 * \param connexionID The connexion ID
	 */
	void RemoveConnexion(std::uint8_t connexionID);

	/**
	 * \brief Broadcast a packet to all connexions
	 * \param packet The packet
	 */
	void BroadcastPacket(const protocol::Packet* packet);
	/**
	 * \brief Broadcast a chat message
	 * \param msg The message
	 */
	void BroadcastChatMessage(const std::string& msg);

	/**
	 * \brief Get the milliseconds per tick
	 * \return The milliseconds per tick
	 */
	float GetMSPT() const {
		return m_TickCounter.GetMSPT();
	}

	/**
	 * \brief Get the ticks per second
	 * \return The ticks per second
	 */
	float GetTPS() const {
		return m_TickCounter.GetTPS();
	}

	/**
	 * \brief Return whether the server is running
	 */
	bool IsRunning() {
		return m_ServerRunning;
	}

	/**
	 * \brief Get the connexions (const version)
	 * \return The connexions
	 */
	const ConnexionMap& GetConnexions() const {
		return m_Connections;
	}

	/**
	 * \brief Get the connexions
	 * \return The connexions
	 */
	ConnexionMap& GetConnexions() {
		return m_Connections;
	}

	/**
	 * \brief Get the game
	 * \return The game
	 */
	ServerGame& GetGame() {
		return m_Game;
	}

	/**
	 * \brief Get the listening port
	 * \return The listening port
	 */
	std::uint16_t GetListeningPort();

	/**
	 * \brief Get a new player ID
	 * \return The new player ID
	 */
	game::PlayerID GetNewPlayerID();

  private:
	void Accept();
	void UpdateSockets();

	void Clean();
	void StartThread();
	void StopThread();
	void ServerLoop();
	void Tick(std::uint64_t delta);
};

}  // namespace server
}  // namespace blitz