#pragma once

#include "blitz/common/Defines.h"
#include "blitz/game/Listeners.h"
#include "blitz/misc/ObjectNotifier.h"
#include "blitz/misc/Time.h"
#include "blitz/protocol/packets/ChatPacket.h"
#include "client/config/BlitzConfig.h"
#include <cstdint>
#include <memory>
#include <string>

namespace blitz {

static const int PlayerUpdatePosRate = 50;

namespace client {
class ClientConnexion;
class ClientGame;
}  // namespace client

namespace server {
class Server;
}  // namespace server

class GuiListener {
  public:
	virtual void OnTextChatReceived(const protocol::ColoredText& text) {}
	virtual void OnSpectatorChange(const game::PlayerID player) {}
};

// Singleton
class Client : public utils::ObjectNotifier<GuiListener>, public game::PlayerListener {
  private:
	std::unique_ptr<server::Server> m_Server;
	std::unique_ptr<client::ClientConnexion> m_Connexion;
	std::unique_ptr<client::ClientGame> m_Game;
	utils::Timer m_UpdatePosTimer{PlayerUpdatePosRate};
	BlitzConfig m_Config;

  public:
	Client();
	virtual ~Client();

	void Update();

	bool IsConnected();

	bool JoinGame(const std::string& pseudo, const std::string& address, std::uint16_t port);
	bool CreateGame(std::uint16_t port, const std::string& pseudo);

	void Disconnect();

	void SendChatText(const std::string& text);
	void ChatTextReceived(const protocol::ColoredText& text);

	void SendPlayerPosAndLook(const Vec3f& position, float yaw, float pitch);

	virtual void OnPlayerShoot(const Vec3f& position, float yaw, float pitch) override;

	game::PlayerID GetPlayerID() const;

	client::ClientGame* GetGame() {
		return m_Game.get();
	}

	BlitzConfig* GetConfig() {
		return &m_Config;
	}

  private:
	void Reset();

	void UpdatePosition(std::uint64_t delta);
};

}  // namespace blitz
