#pragma once

#include "Universe.hpp"
#include "../net/Client.hpp"
#include "../../core/utils/zctx.hpp"

namespace world::client {
    class Area;

    /// Whole universe container in network client
    class DistantUniverse final: public Universe {
    public:
        DistantUniverse(const connection&, const options &, const std::string&);
        ~DistantUniverse();

        void update(voxel_pos, float) override;
        void emit(const action::packet &) override;

        ray_result raycast(const geometry::Ray &) const override;

        bool isAreaFree(const area_<voxel_pos> &pos, geometry::Shape shape, uint16_t radius) const override;

        void getEntitiesModels(const std::function<void(size_t, const std::vector<glm::mat4>&)>&,
            const std::optional<geometry::Frustum>& frustum, const glm::llvec3& offset, int density) override;

        bool isRunning() const override { return peer.isRunning(); }

        size_t getPlayerId() const override { return playerId; }

    protected:
        bool onPacket(const data::out_view&, net::PacketFlags);

        /// Alive areas containing chunks
        area_map areas;

        /// Entities without generation
        struct Entity {
            Entity(glm::usvec3 size, glm::vec3 scale, bool permanant, bool area): size(size), scale(scale), permanant(permanant) {
                if (area) {
                    shape = Universe::Entity::area_t();
                } else {
                    shape = Universe::Entity::model_t();
                }
            }

            std::variant<Universe::Entity::area_t, Universe::Entity::model_t> shape;
            glm::usvec3 size;
            glm::vec3 scale;
            bool permanant;

            data::generational::view_vector<Universe::Entity::Instance> instances;
        };
        data::generational::view_vector<Entity> entities;
        /// Player entity instance index
        size_t playerId;
        std::optional<zstd::read_dict_ctx> dict;

        chunk_pos last_chunk = chunk_pos(INT_MAX);
        bool mayQueryChunks = false;
        uint64_t moveCounter = 0;

        options options;
        uint16_t serverDistance;
        size_t floodfillLimit = CHUNK_SIZE;

        net::client::Client peer;
    };
}