#include <Block/Block.hpp>
#include <Explosion/Explosion.hpp>
#include <cmath>

Explosion::Explosion(LocalWorld &world, glm::ivec3 sourceBlock, double power, int radius) :
    world(world),
    sourceBlock(sourceBlock),
    power(power),
    radius(radius)
{
    glm::ivec3 pos;
    for (pos.z = sourceBlock.z - radius; pos.z <= sourceBlock.z + radius; pos.z += 16)
    {
        for (pos.y = sourceBlock.y - radius; pos.y <= sourceBlock.y + radius; pos.y += 16)
        {
            for (pos.x = sourceBlock.x - radius; pos.x <= sourceBlock.x + radius; pos.x += 16)
            {
                chunks.push_back(world.requestChunk(pos & glm::ivec3(~0xF)));
            }
        }
    }
}

bool Explosion::isReady()
{
    for (const ChunkHandle &chunk : chunks)
    {
        if (!chunk->isReady())
        {
            return false;
        }
    }

    return true;
}

void Explosion::explode()
{
    glm::ivec3 pos;
    for (pos.z = sourceBlock.z - radius; pos.z <= sourceBlock.z + radius; pos.z++)
    {
        for (pos.y = sourceBlock.y - radius; pos.y <= sourceBlock.y + radius; pos.y++)
        {
            for (pos.x = sourceBlock.x - radius; pos.x <= sourceBlock.x + radius; pos.x++)
            {
                // TODO: raycast to determine how much of the explosion is absorbed.

                glm::vec3 delta = glm::vec3(pos - sourceBlock);
                if (glm::length(delta) > radius)
                {
                    continue;
                }

                double powerHere = power * ((double) glm::length(delta) / (double) radius);

                Block *block = world.getBlockAt(pos);
                if (block == nullptr)
                {
                    crash("Explosion affecting an unloaded chunk.");
                }

                if (block->hardness <= powerHere)
                {
                    world.setBlockAt(pos, 0);
                }
            }
        }
    }

    world.playSound(Sound::explosion, sourceBlock);
}