#pragma once

#include "./Abstract.hpp"
#include "../Registry.hpp"

namespace world::generator {
    /// Endless cave network
    class Cave: public Abstract {
    public:
        struct Params {
            Params(int seed = 42, float density = 0, float gran = 30): seed(seed), density(density), granularity(gran) { }

            /// Random generator start
            int seed;
            /// Offset density overrage
            float density;
            /// Speed of density change
            float granularity;
        };
        Cave(const Params p): params(p), density(Noise::SimplexFractal(p.seed)), material(Noise::Cellular(p.seed * 5, .1)) { }

        void generate(const chunk_pos &pos, std::array<Voxel, CHUNK_SIZE> &out) override {
            const auto densitySet = density.getBlock(pos, CHUNK_LENGTH);
            const auto materialSet = material.getBlock(pos, CHUNK_LENGTH);
            constexpr auto MATERIAL_SKIP = 2;
            const int materialMax = module::Registry::Get()->getMaterials().names.size() - (MATERIAL_SKIP + 1);
            for (size_t i = 0; i < CHUNK_SIZE; i++) {
                const auto density = std::clamp((densitySet.get()[i] + params.density) * params.granularity, 0.f, 1.f) * Voxel::DENSITY_MAX;
                const auto material = density > 0 ? MATERIAL_SKIP + std::clamp(static_cast<int>(std::lrint((materialSet.get()[i] + 1) / 2 * materialMax)),
                    0, materialMax) : 1; //NOTE: map (approx -1, 1) to (1, mat_max)
                out[i] = Voxel(material, density);
            }
        }
        glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(-1, 0, 0); }
    private:
        Params params;
        Noise density;
        Noise material;
    };
}