#include "AddEntityUpdate.h"
#include "BasicBlocks.h"
#include "Game.h"
#include "ItemEntity.h"
#include "TreeSeblingBlock.h"

BasicBlock::BasicBlock(
    int typeId, ItemType* zTool, Framework::Vec3<int> pos, int dimensionId)
    : BasicBlock(typeId, zTool, pos, dimensionId, false)
{}

BasicBlock::BasicBlock(int typeId,
    ItemType* zTool,
    Framework::Vec3<int> pos,
    int dimensionId,
    bool hasInventory)
    : Block(typeId, zTool, pos, dimensionId, hasInventory)
{}

bool BasicBlock::onTick(TickQueue* zQueue, int numTicks, bool& blocked)
{
    return 0;
}

void BasicBlock::onPostTick() {}

AdditionalItemSpawningBlock::AdditionalItemSpawningBlock(
    int typeId, ItemType* zTool, Framework::Vec3<int> pos, int dimensionId)
    : BasicBlock(typeId, zTool, pos, dimensionId)
{}

void AdditionalItemSpawningBlock::addSpawn(SpawnConfig config)
{
    spawns.add(config);
}

void AdditionalItemSpawningBlock::onDestroy()
{
    for (const SpawnConfig& config : spawns)
    {
        if ((double)rand() / RAND_MAX < config.chance)
        {
            int amount = config.min
                       + (int)((config.max - config.min)
                               * ((double)rand() / RAND_MAX));
            if (amount > 0)
            {
                ItemStack* spawnedItems = StaticRegistry<ItemType>::INSTANCE
                                              .zElement(config.itemType)
                                              ->createItemStack(amount);
                if (spawnedItems)
                {
                    ItemEntity* itemEntity
                        = (ItemEntity*)StaticRegistry<EntityType>::INSTANCE
                              .zElement(EntityTypeEnum::ITEM)
                              ->createEntity(location
                                                 + Framework::Vec3<float>(
                                                     0.5f, 0.5f, 0.5f),
                                  getDimensionId(),
                                  Game::INSTANCE->getNextEntityId());
                    itemEntity->unsaveAddItem(spawnedItems, NO_DIRECTION, 0);
                    spawnedItems->release();
                    Game::INSTANCE->requestWorldUpdate(
                        new AddEntityUpdate(itemEntity, getDimensionId()));
                }
            }
        }
    }
    BasicBlock::onDestroy();
}

BasicBlockType::BasicBlockType(
    int typeId, int itemTypeId, ModelInfo model, const char* name, int mapColor)
    : BasicBlockType(
        typeId,
        itemTypeId,
        model,
        [typeId](Framework::Vec3<int> pos, int dimensionId) {
            return new BasicBlock(typeId, 0, pos, dimensionId);
        },
        name,
        mapColor)
{}

BasicBlockType::BasicBlockType(int typeId,
    int itemTypeId,
    ModelInfo model,
    std::function<Block*(Framework::Vec3<int>, int)> creatBlockCustom,
    const char* name,
    int mapColor)
    : BasicBlockType(
        typeId, itemTypeId, model, creatBlockCustom, name, mapColor, 0)
{}

BasicBlockType::BasicBlockType(int typeId,
    int itemTypeId,
    ModelInfo model,
    std::function<Block* (Framework::Vec3<int>, int)> creatBlockCustom,
    const char* name,
    int mapColor,
    bool modelSubscription)
    : BlockType(typeId, 0, model, 1, 100, 0, name, modelSubscription, mapColor),
      itemType(itemTypeId),
      transparent(0),
      passable(0),
      hardness(1.f),
      zTool(0),
      speedModifier(1.f),
      interactable(1),
      creatBlockCustom(creatBlockCustom)
{}

void BasicBlockType::createSuperBlock(Block* zBlock, Item* zItem) const
{
    BasicBlock* block = dynamic_cast<BasicBlock*>(zBlock);
    block->transparent = transparent;
    block->passable = passable;
    block->hp = (float)getInitialMaxHP();
    block->maxHP = (float)getInitialMaxHP();
    block->hardness = hardness;
    block->zTool = zTool;
    block->speedModifier = speedModifier;
    block->interactable = interactable;
    BlockType::createSuperBlock(zBlock, zItem);
}

Block* BasicBlockType::createBlock(
    Framework::Vec3<int> position, int dimensionId) const
{
    return creatBlockCustom(position, dimensionId);
}

Item* BasicBlockType::createItem() const
{
    return StaticRegistry<ItemType>::INSTANCE.zElement(itemType)->createItem();
}

BasicBlockType* BasicBlockType::setHardness(float hardness)
{
    this->hardness = hardness;
    return this;
}

BasicBlockType* BasicBlockType::setTransparent(bool transparent)
{
    this->transparent = transparent;
    return this;
}