#pragma once

#include <Array.h>
#include <Critical.h>
#include <Trie.h>

#include "Chunk.h"
#include "Entity.h"

class World;

class Dimension : public virtual Framework::Model3DCollection
{
private:
    int id;
    Framework::RCTrie<Chunk>* chunks;
    Framework::Array<Chunk*> chunkList;
    Framework::RCArray<Entity>* entities;
    Framework::Critical cs;
    void getAddrOf(Framework::Punkt cPos, char* addr) const;
    void getAddrOfWorld(Framework::Punkt wPos, char* addr) const;

public:
    Dimension();
    ~Dimension();

    void forAll(std::function<void(Model3D*)> f) override;
    void render(std::function<void(Model3D*)> f) override;
    bool tick(std::function<void(Model3D*)> f, double time) override;

    void setId(int id);
    void api(char* message);
    Block* zBlock(Framework::Vec3<int> location);
    Block* getBlock(Framework::Vec3<int> location);
    void addEntity(Entity* entity);
    void setChunk(Chunk* chunk, Framework::Punkt center);
    bool hasChunck(int x, int y) const;
    Chunk* zChunk(Framework::Punkt wPos) const;
    void removeDistantChunks(Framework::Punkt wPos);
    void setBlock(Block* block);
    void removeBlock(Block* zBlock);
    Entity* zEntity(int id);
    Entity* getEntity(int id);
    void removeEntity(int id);
    int getId() const;

    inline static Framework::Vec3<int> chunkCoordinates(
        Framework::Vec3<int> worldLocation)
    {
        Framework::Vec3<int> result = worldLocation;
        result.x = result.x % CHUNK_SIZE;
        result.y = result.y % CHUNK_SIZE;
        if (result.x < 0) result.x += CHUNK_SIZE;
        if (result.y < 0) result.y += CHUNK_SIZE;
        return result;
    }
};