#pragma once

#include "../../core/world/Universe.hpp"
#include "../../core/world/actions.hpp"
#include "../../core/net/data.hpp"
#include <memory>

namespace contouring {
    class Abstract;
}
namespace geometry {
    struct Frustum;
}
namespace world::client {
    /// Whole universe container in abstract client
    class Universe: public world::Universe {
    public:
        Universe(const std::string& contouring);
        virtual ~Universe();

        /// Options for distant universe
        struct options: world::Universe::options {
            /// Render temporary partial average as placeholder
            bool useAverages = false;
            /// Dont query chunks with absolute majorant average
            /// Avoid querying whole space but may cause missed edits in those chunks
            bool trustMajorant = true;
            /// Apply edits locally while sending
            bool editPrediction = true;
            /// Get only edits commands and compute locally
            bool editHandling = true;
            /// Apply velocity locally. Smooth entities positions
            bool movePrediction = true;
            /// Check for entities collisions
            bool moveCollision = false;
        };
        struct connection: net::address {
            connection(): net::address{"127.0.0.1", 4242} { }
        };

        /// Update edits and contouring
        virtual void update(voxel_pos pos, float deltaTime) = 0;

        /// Send action to ServerUniverse
        virtual void emit(const action::packet &) = 0;
        /// When server teleport player
        std::function<void(voxel_pos)> onTeleport;
        /// On chat message
        std::function<void(const std::string&)> onMessage;

        /// Get current contouring worker
        contouring::Abstract* getContouring() const {
            return contouring.get();
        }

        /// Get entities in view
        virtual 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) = 0;

        virtual size_t getPlayerId() const = 0;

        virtual bool isRunning() const = 0;

    protected:
        /// Contouring worker
        std::unique_ptr<contouring::Abstract> contouring;
    };
}