#ifndef ENTITY_HPP_
#define ENTITY_HPP_

#include <cstdint>
#include <cstddef>

#include <GraphicsContext/GraphicsContext.hpp>
#include <Serialize/Serialize.hpp>

class World;
class Block;
class WorldDisplay;

/**
 * @brief Represents an entity.
 */
class Entity
{
protected:
    World* const world;

    /**
     * @brief Send a message.
     * 
     * If this is called on a client, the entity's server side receives the message;
     * if this is called on the server, all clients receive the message.
     * 
     * @param msg The message.
     * @param size The message size.
     */
    void sendMessage(const void *msg, size_t size);

    /**
     * @brief Send a plain message (raw-copy all bytes).
     * 
     * @tparam TMessage The message type (must be a plain struct).
     * @param msg The message.
     */
    template<typename TMessage>
    void sendPlainMessage(const TMessage &msg)
    {
        sendMessage(&msg, sizeof(TMessage));
    }

    template<typename... TArgs>
    void sendSerialMessage(TArgs... args)
    {
        std::vector<uint8_t> data = serialize(args...);
        sendMessage(data.data(), data.size());
    }

    /**
     * @brief Despawn this entity.
     */
    void despawn();

public:
    const std::string typeId;
    const uint64_t id;

    /**
     * @brief Construct an entity for the specified world.
     * 
     * @param world The world.
     * @param typeId The type ID.
     * @param id The ID.
     */
    Entity(World &world, std::string typeId, uint64_t id);

    virtual ~Entity() = default;

    /**
     * @brief Called on each update.
     */
    virtual void update() = 0;

    /**
     * @brief Draw the entity.
     * 
     * @param context The graphics context.
     * @param display The world display.
     */
    virtual void draw(GraphicsContext &context, WorldDisplay &display) = 0;

    /**
     * @brief Called when a message is received.
     * 
     * On the server, this is called when a client sends a message; on a client,
     * this is called when a message arrives from the server.
     * 
     * @param msg The message.
     * @param size The message size.
     */
    virtual void onMessage(const void *msg, size_t size) = 0;

    /**
     * @brief Serialize the entity.
     * 
     * This is used to inform clients of the current state of entities when logging
     * in.
     * 
     * @param outBytes The vector into which the entity will be output.
     */
    virtual void serializeState(std::vector<uint8_t> &outBytes) = 0;

    /**
     * @brief Deserialize the entity from data.
     * 
     * This is used to receive entity state from the server.
     * 
     * @param data Data pointer.
     * @param size Size.
     */
    virtual size_t deserializeState(const uint8_t *data, size_t size) = 0;

    /**
     * @brief Returns true if this entity would collide with a block at the given coordinates.
     * 
     * Returns `false` by default.
     */
    virtual bool wouldCollideWith(Block &block, glm::ivec3 pos);
};

#endif