#pragma once
#include "VoxelTypes.h"
#include <unordered_map>
#include <vector>
#include <memory>
#include <mutex>

namespace voxel {

// ── Chunk ───────────────────────────────────────────────────────
struct Chunk {
    VoxelData voxels[CHUNK_VOLUME];
    ChunkPos pos;
    bool dirty = true;

    // Mesh data (output of greedy mesher, sorted by face ID 0-5)
    std::vector<PackedQuad> quads;
    uint32_t quadCount = 0;
    uint32_t faceOffsets[6] = {}; // offset (in quads) for each face group within quads[]
    uint32_t faceCounts[6] = {};  // number of quads per face group

    // Smooth mesh data (output of Surface Nets mesher, Phase 5)
    std::vector<SmoothVertex> smoothVertices;
    uint32_t smoothVertexCount = 0;
    bool hasSmooth = false; // true if chunk has smooth mesh output (set by mesher)
    bool containsSmooth = false; // true if chunk contains any FLAG_SMOOTH voxels (set during generation)

    VoxelData& at(int x, int y, int z) {
        return voxels[x + y * CHUNK_SIZE + z * CHUNK_SIZE * CHUNK_SIZE];
    }

    const VoxelData& at(int x, int y, int z) const {
        return voxels[x + y * CHUNK_SIZE + z * CHUNK_SIZE * CHUNK_SIZE];
    }

    bool isInBounds(int x, int y, int z) const {
        return x >= 0 && x < CHUNK_SIZE &&
               y >= 0 && y < CHUNK_SIZE &&
               z >= 0 && z < CHUNK_SIZE;
    }
};

// ── World ───────────────────────────────────────────────────────
class VoxelWorld {
public:
    VoxelWorld();
    ~VoxelWorld();

    // Generate a procedural world around a center position
    void generateAround(float cx, float cy, float cz, int radiusChunks);

    // Regenerate all chunks with animated noise (wave effect)
    // If packDst is non-null, each chunk's voxel data is memcpy'd into it
    // at offset [chunkIndex * CHUNK_VOLUME/2] (packed uint16 pairs as uint32).
    void regenerateAnimated(float time, uint32_t* packDst = nullptr, uint32_t packDstCapacity = 0);

    // Generate debug world: isolated blocks for face visibility testing
    void generateDebug();

    // Generate debug world for smooth blending: isolated material transitions
    void generateDebugSmooth();

    // Get a chunk (nullptr if not loaded)
    Chunk* getChunk(const ChunkPos& pos);
    const Chunk* getChunk(const ChunkPos& pos) const;

    // Get voxel at world position (handles cross-chunk lookup)
    VoxelData getVoxel(int wx, int wy, int wz) const;
    void setVoxel(int wx, int wy, int wz, VoxelData v);

    // Iterate all chunks
    template<typename Fn>
    void forEachChunk(Fn&& fn) {
        for (auto& [pos, chunk] : chunks_) {
            fn(pos, *chunk);
        }
    }

    template<typename Fn>
    void forEachChunk(Fn&& fn) const {
        for (auto& [pos, chunk] : chunks_) {
            fn(pos, *chunk);
        }
    }

    size_t chunkCount() const { return chunks_.size(); }

    // Material palette
    MaterialDesc materials[256];
    void setupDefaultMaterials();

private:
    void generateChunk(Chunk& chunk, float timeOffset = 0.0f);
    float noise3D(float x, float y, float z) const;
    float fbm(float x, float y, float z, int octaves) const;

    std::unordered_map<ChunkPos, std::unique_ptr<Chunk>, ChunkPosHash> chunks_;
    uint32_t seed_ = 42;
};

} // namespace voxel
