#pragma once

#include <Array.h>
#include <Critical.h>
#include <Model3D.h>
#include <Model3DCollection.h>
#include <Punkt.h>
#include <Reader.h>

#include "Area.h"
#include "Block.h"
#include "BlockAnimation.h"
#include "Constants.h"
#include "FactoryCraftModel.h"

struct GroundModelPart
{
    int* indexList;
    int indexCount;
    int indexArraySize;
    Text name;
};

class Chunk : public Framework::Model3DCollection
{
private:
    Framework::Punkt location;
    // TODO: use native array for bedder performance?
    Block** blocks;
    Framework::Array<Block*> visibleBlocks;
    FactoryCraftModel* groundModel;
    bool isLoading;
    Framework::Critical cs;
    Framework::Critical vcs;
    Framework::Critical acs;
    Framework::RCArray<BlockAnimation> animations;
    bool lightChanged;
    bool modelChanged;

    void appendAnimation(
        Block* zB, int boneId, double time, Vec3<float> pos, Vec3<float> rot);
    void load(Framework::StreamReader* zReader);
    void buildGroundModel();
    void updateGroundLight();
    __int64 calculateLight(
        Vec3<float> vertexPos, Vec3<int> blockPos, Direction direction);
    bool isPartOfGroundModel(Framework::Vec3<int> location, int directionIndex);

public:
    Chunk(Framework::Punkt location);
    Chunk(Framework::Punkt location, Framework::StreamReader* zReader);
    ~Chunk();
    void destroy();
    void api(char* message);
    Block* zBlockAt(Framework::Vec3<int> cLocation);
    void setBlock(Block* block);
    void removeBlock(Block* zBlock);
    void blockVisibilityChanged(Block* zB);
    Framework::Punkt getCenter() const;
    Framework::Vec3<int> getMin() const;
    Framework::Vec3<int> getMax() const;
    void forAll(std::function<void(Model3D*)> f) override;
    bool tick(std::function<void(Model3D*)> f, double time) override;
    void setLightChanged();

    inline static int index(Framework::Vec3<int> localLocation)
    {
        return (localLocation.x * CHUNK_SIZE + localLocation.y) * WORLD_HEIGHT
             + localLocation.z;
    }
};