#include <Item/StatefulItem/BasicTool/Axe/Axe.hpp>
#include <Block/Block.hpp>
#include <World/LocalWorld/LocalWorld.hpp>
#include <Utilities/Utilities.hpp>
#include <Decoration/Tree/Tree.hpp>
#include <algorithm>
#include <vector>

bool Axe::isEffectiveAgainst(Block &block)
{
    return block.name == "main:leaves" ||
        block.isSuffixed("Leaves") ||
        block.name == "main:planks" ||
        block.isSuffixed("Log") ||
        block.isSuffixed("Planks");
}

Axe::Axe(std::string name, Material &mat, int numUses, float power, std::string displayName) :
    BasicTool(name, mat, numUses),
    m_power(power),
    m_displayName(displayName)
{}

bool Axe::canDig(ItemStack &stack, Block &block)
{
    return block.canManuallyDig() || ((isEffectiveAgainst(block) || block.isPrefixed("planks")) && uses(stack) != m_maxUses);
}

float Axe::getPowerAgainst(ItemStack &stack, Block &block)
{
    return isEffectiveAgainst(block) ? m_power : 1.0f;
}

std::string Axe::getDisplayName(const ItemStack &stack)
{
    return m_displayName;
}

void Axe::harvest(ItemStack &stack, LocalWorld &world, glm::ivec3 pos)
{
    Block *block = world.getBlockAt(pos);
    uint8_t meta = *world.getBlockMeta(pos);

    if (block->canManuallyDig())
    {
        world.breakBlock(pos);
        return;
    }

    uses(stack)++;

    if (!isEffectiveAgainst(*block))
    {
        world.breakBlock(pos);
        return;
    }

    if (block->name != "main:leaves" && !block->isSuffixed("Leaves") && (meta & Tree::metaNaturalLogMask) == 0)
    {
        world.breakBlock(pos);
        return;
    }

    walkTree(world, pos, [&world](const glm::ivec3 &pos)
    {
        world.breakBlock(pos);
        return true;
    });
}

void Axe::getExtraToolTipLines(const ItemStack &stack, ToolTip &tip)
{
    BasicTool::getExtraToolTipLines(stack, tip);
    tip.addLine(formatString("Power: $+$+", GraphicsContext::colorstr(Color::toolPower), m_power));
}

Sound& Axe::getDigSoundFor(ItemStack &stack, Block &block)
{
    return Sound::axe;
}

void Axe::getSelectedBlocks(
    ItemStack &stack,
    Player &player,
    World &world,
    glm::ivec3 primaryBlockPos,
    std::vector<glm::ivec3> &selectedBlocks
)
{
    Block *block = world.getBlockAt(primaryBlockPos);
    uint8_t meta = *world.getBlockMeta(primaryBlockPos);

    if (block->canManuallyDig())
    {
        ITool::getSelectedBlocks(stack, player, world, primaryBlockPos, selectedBlocks);
        return;
    }

    if (!isEffectiveAgainst(*block))
    {
        ITool::getSelectedBlocks(stack, player, world, primaryBlockPos, selectedBlocks);
        return;
    }

    if (block->name != "main:leaves" && !block->isSuffixed("Leaves") && (meta & Tree::metaNaturalLogMask) == 0)
    {
        ITool::getSelectedBlocks(stack, player, world, primaryBlockPos, selectedBlocks);
        return;
    }

    std::vector<glm::ivec3> visitedBlocks;
    walkTree(world, primaryBlockPos, [&world, &selectedBlocks, &visitedBlocks](const glm::ivec3 &pos)
    {
        if (std::find(visitedBlocks.begin(), visitedBlocks.end(), pos) != visitedBlocks.end())
        {
            return false;
        }

        visitedBlocks.push_back(pos);
        Block *block = world.getBlockAt(pos);
        if (block == nullptr)
        {
            return false;
        }

        selectedBlocks.push_back(pos);
        return true;
    });
}