#include <DecorationGenerator/DecorationGenerator.hpp>
#include <DecorationRegistry/DecorationRegistry.hpp>
#include <WorldGenerator/SurvivalWorldGenerator/SurvivalWorldGenerator.hpp>
#include <Decoration/Decoration.hpp>

void DecorationGenerator::generateSpawnedFromChunk(Decoration::Builder &builder, int chunkX, int chunkY)
{
    uint64_t key = ((uint64_t) (uint32_t) chunkX << 32) | (uint64_t) (uint32_t) chunkY;
    GeneratorNode chunkNode = decorationNode[key];

    glm::ivec3 offset;
    for (offset.y = 0; offset.y < 16; offset.y++)
    {
        for (offset.x = 0; offset.x < 16; offset.x++)
        {
            glm::ivec3 pos = glm::ivec3(chunkX, chunkY, 0) + offset;

            TerrainGenerator::Cell &cell = worldgen.terrain.getCell(pos.x, pos.y);
            pos.z = cell.height + 1;

            if (pos.z < 0 || worldgen.tunnels.isInsideTunnel(pos + glm::ivec3(0, 0, -1)))
            {
                // Below sea level or inside a tunnel.
                continue;
            }

            GeneratorNode thisNode = chunkNode(offset.y * 16 + offset.x);
            double chanceOffset = (double) (thisNode.seed & 0xFFFF) / (double) 0xFFFF;

            for (const std::shared_ptr<Decoration> &decoration : decorationRegistry.decorations)
            {
                double probabilityDensity = (double) decoration->getAverageDensity(cell, thisNode) / (double) (16*16);
                if (chanceOffset < probabilityDensity)
                {
                    decoration->build(builder, thisNode, pos);
                    break;
                }

                chanceOffset -= probabilityDensity;
            }
        }
    }
}

DecorationGenerator::DecorationGenerator(SurvivalWorldGenerator &worldgen) :
    worldgen(worldgen),
    decorationNode(worldgen.getRootNode()[0x0bbd9150474c3fbc])
{
    decorationRegistry.registerAllDecorations(worldgen.getWorld());
}

void DecorationGenerator::generate(Chunk &chunk)
{
    if (chunk.base.z < -16)
    {
        return;
    }

    Decoration::Builder builder(worldgen, chunk);

    int offsetX, offsetY;
    for (offsetX=-16; offsetX<=16; offsetX+=16)
    {
        for (offsetY=-16; offsetY<=16; offsetY+=16)
        {
            generateSpawnedFromChunk(builder, chunk.base.x + offsetX, chunk.base.y + offsetY);
        }
    }
}