#ifndef CHUNK_WINDOW_HPP_
#define CHUNK_WINDOW_HPP_

#include <Chunk/Chunk.hpp>
#include <ChunkHandle/ChunkHandle.hpp>
#include <BoundingBox/BoundingBox.hpp>
#include <Utilities/CircularInt.hpp>
#include <VoxelTree/VoxelTree.hpp>
#include <World/World.hpp>
#include <Entity/PhysicsEntity/CameraEntity/Player/Player.hpp>
#include <PipelineStage/DrawStage/ParticlesStage/ParticlesStage.hpp>
#include <vector>

class WorldDisplay;

/**
 * @brief Manages the encoding of in-range chunks into the SVO.
 */
class ChunkWindow final
{
private:
    std::vector<ChunkHandle> loadingChunks;
    std::array<std::vector<ChunkHandle>, 32> encodingChunks;  // indexed by distance from player in chunks
    std::vector<ChunkHandle> visibleChunks;
    std::vector<std::shared_ptr<Chunk>> chunksWithEffects;
    BoundingBox<cint32> visibleBox;
    GPUMirroredArray<ParticlesStage::Particle> particlePool;
    std::vector<size_t> particleReclaimStack;
    glm::uvec3 currentBaseVoxel;
    bool particlePoolScannedThisFrame = false;

    void requestChunk(glm::ivec3 base);
    void requestBox(const BoundingBox<cint32> &box);
    void detachAll();
    void checkLoadingChunks();
    void checkEncodingChunks();
    bool isChunkInView(Chunk &chunk);
    void shrinkToView(const BoundingBox<cint32> &box);
    void expandToView(const BoundingBox<cint32> &box);
    void rescanParticlePool();

public:
    World &world;
    std::shared_ptr<Player> player;
    VoxelTree svo;
    WorldDisplay &worldDisplay;

    ChunkWindow(World &world, std::shared_ptr<Player> player, WorldDisplay &worldDisplay);

    void setVisibleBox(const BoundingBox<cint32> &visibleBox);
    void update(WorldDisplay &display);
    void draw(WorldDisplay &display);
    void purge();
    void emitParticle(ParticlesStage::Particle particle);
    void addChunkWithEffects(std::shared_ptr<Chunk> chunk);
    LightingStage::LightID addBlockLight(LightingStage::LightSource lightSource);
    void setBlockLight(LightingStage::LightID id, LightingStage::LightSource lightSource);
    void releaseBlockLight(LightingStage::LightID id);
    size_t getActiveLights();
};

#endif