#ifndef WORLD_GRID_HPP_
#define WORLD_GRID_HPP_

#include <Utilities/CircularInt.hpp>
#include <memory>
#include <WorldGridNode/WorldGridNode.hpp>
#include <Chunk/Chunk.hpp>
#include <BoundingBox/BoundingBox.hpp>
#include <ChunkHandle/ChunkHandle.hpp>

class World;
class WorldDisplay;

/**
 * @brief The world grid.
 */
class WorldGrid
{
private:
    World &world;
    std::shared_ptr<WorldGridNode> rootNode;

    void checkRootNode();

public:
    /**
     * @brief Construct the world grid for the specified world.
     */
    WorldGrid(World &world);

    /**
     * @brief Return the chunk at the specified position.
     * 
     * @param pos The position of the chunk.
     * @param hint An optional hint on where to begin the search.
     * @return The chunk.
     */
    std::shared_ptr<Chunk> getChunk(glm::ivec3 pos, std::shared_ptr<WorldGridNode> hint = nullptr);

    /**
     * @brief Request and return a chunk.
     * 
     * The chunk will begin loading if it was not previously requested. The chunk will remain
     * loaded for as long as at least one handle to it exists.
     * 
     * @param pos The position of the chunk.
     * @param hint An optional hint on where to begin the search.
     * @param relativeDeadline The relative deadline: defaults to 0.0f (highest priority).
     * @return The chunk handle which keeps the chunk retained.
     */
    ChunkHandle requestChunk(glm::ivec3 pos, std::shared_ptr<WorldGridNode> hint = nullptr, float relativeDeadline = 0.0f);

    /**
     * @brief Update the retain counts of chunks within a box.
     * 
     * If `delta` is 1 and one of the chunks is not loaded, it will start to load. If `delta` is -1
     * and a chunk's count reaches 0, it will be added to the purge queue.
     * 
     * @param world The world.
     * @param box The bounding box.
     * @param delta What value to change by (-1 or 1).
     * @param onHit Callback to invoke with each chunk in the box.
     */
    void updateChunkCountsWithinBox(
        World &world,
        const BoundingBox<cint32> &box,
        int delta,
        const std::function<void(Chunk&)> &onHit
    );

    /**
     * @brief Returns the root node.
     */
    std::shared_ptr<WorldGridNode> getRootNode();
};

#endif