#include <WorldGenerator/SurvivalWorldGenerator/SurvivalWorldGenerator.hpp>
#include <MantleGenerator/MantleGenerator.hpp>
#include <World/LocalWorld/LocalWorld.hpp>
#include <Side/Side.hpp>

MantleGenerator::MantleGenerator(SurvivalWorldGenerator &worldgen) :
    worldgen(worldgen),
    crystalNode(worldgen.getRootNode()[0x242e5b1a33be67f7])
{}

void MantleGenerator::generate(Chunk &chunk)
{
    if (chunk.base.z == -128)
    {
        int ringwooditeID = worldgen.getWorld().getBlockId("main:ringwoodite");

        glm::ivec3 pos;
        for (pos.z=0; pos.z<3; pos.z++)
        {
            for (pos.y=0; pos.y<16; pos.y++)
            {
                for (pos.x=0; pos.x<16; pos.x++)
                {
                    chunk.setBlockAndMeta(pos, ringwooditeID, 0);
                }
            }
        }
    }
    else if (chunk.base.z < -128)
    {
        std::vector<glm::ivec4> crystalCandidatePositions;

        glm::ivec4 pos;
        for (pos.z=0; pos.z<15; pos.z++)
        {
            for (pos.y=0; pos.y<15; pos.y++)
            {
                for (pos.x=0; pos.x<15; pos.x++)
                {
                    if (std::get<0>(chunk.getBlockAndMeta(pos)) != 0)
                    {
                        continue;
                    }

                    pos.w = 0;

                    for (const Side &side : Side::all)
                    {
                        pos.w = side.asNumber();

                        glm::ivec3 otherPos = glm::ivec3(pos) + side.out();
                        if ((otherPos & ~0xF) != glm::ivec3(0) || std::get<0>(chunk.getBlockAndMeta(otherPos)) == 0)
                        {
                            continue;
                        }

                        crystalCandidatePositions.push_back(pos);
                    }
                }
            }
        }

        // Set up the generator node.
        if (crystalCandidatePositions.size() == 0)
        {
            return;
        }

        uint64_t key = ((uint64_t) (uint32_t) chunk.base.x << 32) | (uint64_t) (uint32_t) chunk.base.y;
        uint64_t key2 = (uint64_t) (uint32_t) chunk.base.z;

        GeneratorNode genNode = crystalNode(key, key2);

        // Generate a crystal.
        std::string crystalName;
        uint64_t chanceValue = genNode.seed % 100;
        if (chanceValue < 20)
        {
            crystalName = "main:quartzCrystal";
        }
        else if (chanceValue < 50)
        {
            crystalName = "main:emeraldCrystal";
        }
        else
        {
            crystalName = "main:lapisCrystal";
        }

        int idCrystal = worldgen.getWorld().getBlockId(crystalName);

        size_t index = genNode[0] % crystalCandidatePositions.size();
        glm::ivec4 candidate = crystalCandidatePositions[index];

        chunk.setBlockAndMeta(candidate, idCrystal, candidate.w);
    }
}