#ifndef REMOTE_WORLD_HPP_
#define REMOTE_WORLD_HPP_

#include <World/World.hpp>
#include <Utilities/Socket.hpp>
#include <Messenger/Messenger.hpp>
#include <CommandQueue/CommandQueue.hpp>
#include <Protocol/Protocol.hpp>

/**
 * @brief Represents a remote world.
 */
class RemoteWorld final : public World
{
private:
    SOCKET socket;
    std::shared_ptr<Messenger> messenger;
    std::shared_ptr<Player> ourPlayer;
    CommandQueue<RemoteWorld> commandQueue;
    Uint32 lastKeepAliveTicks;
    Uint32 pingTicks = 0;
    ChunkWindow *chunkWindow = nullptr;
    BoundingBox<cint32> visibleChunkBox;

    uint64_t syncWorldTime;
    Uint64 syncTicks;

    std::atomic<bool> isRequestingDisconnect;
    std::atomic<bool> receivingThreadEnded;
    std::set<std::shared_ptr<Chunk>> requestedChunks;

    void onMessage(Protocol::MessageType type, void *payload, uint32_t payloadSize);
    void reportFatalError(std::string error);

public:
    /**
     * @brief Connection data which needs to be passed to the constructor.
     */
    struct ConnectionData
    {
        SOCKET socket;
        uint64_t playerEntityID;
        std::string userName;
        char* playerData;
        size_t playerDataSize;
        std::vector<std::string> blockTable;
        int timeOfDay;
        std::vector<uint8_t> beltData;
        GeneratorNode weatherNode;
        uint64_t worldTime;
    };

    /**
     * @brief Names of currently-online players.
     */
    std::vector<std::string> playerNames;

    /**
     * @brief Construct a remote world.
     * 
     * @param socket The connection to the server.
     */
    RemoteWorld(const ConnectionData &data);

    ~RemoteWorld();

    /**
     * @brief Get the player which we are controlling.
     */
    std::shared_ptr<Player> getOurPlayer();

    virtual void sendMessageToEntity(uint64_t id, const void *msg, size_t size) 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 drawPlayerModel(GraphicsContext &context, WorldDisplay &worldDisplay, Player &player) override;
    virtual void onPlayerMessage(Player &player, const void *msg, size_t size) override;
    virtual void onChunkRequested(std::shared_ptr<Chunk> chunk, float relativeDeadline) override;
    virtual void close() override;
    virtual void fastUpdate(ChunkWindow *chunkWindow) override;
    virtual void update() override;
    virtual bool isLocal() override;
    virtual void drawBlockSelector(GraphicsContext &context, WorldDisplay &worldDisplay, Player &player) override;
    virtual bool canPurgeChunk(Chunk &chunk) override;
    virtual void onWorldGridNodeCreated(std::shared_ptr<WorldGridNode> node) override;
    virtual void playSound(Sound &sound, glm::ivec3 blockPos, glm::vec3 vel) override;
    virtual void writeDebugStats(std::ostream &os) override;
    virtual bool isShutdownReady() override;
    virtual uint64_t getWorldTime() override;
    virtual void getWorldStats(StatList &statList) override;
    virtual std::shared_ptr<Messenger> getPeerMessenger(Player &player) override;
};

#endif