#pragma once

#include <map>
#include <thread>

#include "blitz/misc/TickCounter.h"
#include "blitz/network/TCPListener.h"
#include "blitz/protocol/PacketDispatcher.h"
#include "blitz/protocol/PacketHandler.h"
#include "blitz/protocol/Protocol.h"
#include "server/ServerConnexion.h"

namespace blitz {

namespace game {

class Game;

}  // namespace game


namespace server {

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

class Server {
  private:
	network::TCPListener m_Listener;
	ConnexionMap m_Connections;
	utils::TickCounter m_TickCounter;

	std::unique_ptr<game::Game> m_Game;

	std::thread m_Thread;
	bool m_ServerRunning;

  public:
	Server();
	virtual ~Server();

	bool Start(std::uint16_t port, bool blocking);
	void Stop();  // force the server to stop

	void Restart();

	void RemoveConnexion(std::uint8_t connexionID);

	void BroadcastPacket(const protocol::Packet* packet);
	void BroadcastChatMessage(const std::string& msg);

	float GetMSPT() const {
		return m_TickCounter.GetMSPT();
	}

	float GetTPS() const {
		return m_TickCounter.GetTPS();
	}

	bool IsRunning() {
		return m_ServerRunning;
	}

	const ConnexionMap& GetConnexions() const {
		return m_Connections;
	}

	ConnexionMap& GetConnexions() {
		return m_Connections;
	}

	game::Game* GetGame() {
		return m_Game.get();
	}

	std::uint16_t GetListeningPort();

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

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

}  // namespace server
}  // namespace blitz