#ifndef SERVER_WORLD_HPP_
#define SERVER_WORLD_HPP_

#include <World/LocalWorld/LocalWorld.hpp>
#include <Messenger/Messenger.hpp>
#include <Protocol/Protocol.hpp>
#include <UserDB/UserDB.hpp>
#include <Utilities/Extern.hpp>

/**
 * @brief The server side of a multiplayer world.
 */
class ServerWorld final : public LocalWorld
{
public:
    using TaskCallback = std::function<void()>;
    using ScheduleTaskCallback = std::function<void(TaskCallback)>;

private:
    std::vector<Protocol::BlockUpdate> m_blockUpdates;
    std::unordered_map<std::string, std::shared_ptr<Player>> m_players;
    ScheduleTaskCallback scheduleTask;

public:
    /**
     * @brief Construct a server world.
     * 
     * @param worldDir The world directory.
     */
    ServerWorld(std::string worldDir, ScheduleTaskCallback scheduleTask);

    /**
     * @brief Add a new player to the game.
     * 
     * This is called after the player has been authenticated. If this player is already
     * in the game, the existing instance of this player is kicked.
     * 
     * @param userName The user name.
     * @return The player entity.
     */
    std::shared_ptr<Player> addPlayer(std::string userName, UserDB::PermissionSet perms);

    /**
     * @brief Remove the specified player from the game.
     * 
     * @param userName The user name.
     */
    void removePlayer(std::string userName);

    /**
     * @brief Returns the list of player names who are currently online.
     */
    std::vector<std::string> getPlayerNames();

    /**
     * @brief Returns the names of players currently typing.
     *
     * @param skipPlayer The name of a player to skip (usually the querying player).
     */
    std::vector<std::string> getTypingPlayerNames(const std::string &skipPlayer);

    /**
     * @brief Serialize the belt view table into the specified vector.
     */
    void serializeBeltViews(std::vector<uint8_t> &outBytes);

    /**
     * @brief Send a chat line to all players.
     */
    void sendChatToAllPlayers(std::string text, glm::vec3 color = glm::vec3(1.0f));

    virtual void onChunkLoaded(std::shared_ptr<Chunk> chunk) override;
    virtual void sendMessageToEntity(uint64_t id, const void *msg, size_t size) override;
    virtual void fastUpdate(ChunkWindow *chunkWindow) override;
    virtual void update() override;
    virtual void onBlockGridUpdated(glm::ivec3 pos, int id, uint8_t meta) override;
    virtual void sendCreateEntityMessageToClients(std::shared_ptr<Entity> entity) override;
    virtual void sendDeleteEntityMessageToClients(uint64_t id) override;
    virtual void onPlayerUpdateBegins(Player &player) override;
    virtual void onPlayerUpdateOver(Player &player) override;
    virtual void onPlayerMessage(Player &player, const void *msg, size_t size) override;
    virtual void playSound(Sound &sound, glm::ivec3 blockPos, glm::vec3 vel) override;
    virtual void onBeltViewUpdated(BeltView &view) override;
    virtual void onBeltViewRemoved(uint64_t id) override;
    virtual void close() override;
    virtual bool isShutdownReady() override;
    virtual std::shared_ptr<Player> getPlayer(const std::string &userName) override;
};

#endif