#include "SpecificItemDrop.h"

#include "Block.h"
#include "Entity.h"
#include "Inventory.h"
#include "ItemStack.h"

SpecificItemDrop::SpecificItemDrop(
    Framework::Text itemTypeName, ItemModifier* modifier, int amount)
    : DropConfig(),
      itemTypeName(itemTypeName),
      zType(0),
      modifier(modifier),
      amount(amount)
{}

SpecificItemDrop::~SpecificItemDrop()
{
    if (modifier) modifier->release();
}

void SpecificItemDrop::initialize()
{
    if (!zType)
    {
        int id = Game::INSTANCE->getItemTypeId(itemTypeName);
        if (id >= 0)
        {
            zType = Game::INSTANCE->zItemType(id);
        }
    }
}

Framework::Text SpecificItemDrop::getItemTypeName() const
{
    return itemTypeName;
}

const ItemModifier* SpecificItemDrop::zModifier() const
{
    return modifier;
}

int SpecificItemDrop::getAmount() const
{
    return amount;
}

void SpecificItemDrop::doDrop(Entity* zActor,
    Item* zItem,
    ItemSkill* zUsedSkill,
    Framework::Either<Block*, Entity*> zDestroyedObject) const
{
    if (zType)
    {
        Item* item = zType->createItem();
        if (item)
        {
            if (modifier)
            {
                modifier->applyOn(item);
            }
            ItemStack* stack = new ItemStack(item, amount);
            Inventory* inventory;
            if (zDestroyedObject.isA())
            {
                inventory = dynamic_cast<Inventory*>(zDestroyedObject.getA());
            }
            else
            {
                inventory = dynamic_cast<Inventory*>(zDestroyedObject.getB());
            }
            Game::INSTANCE->spawnItem(
                inventory->getLocation()
                    + Framework::Vec3<float>(0.5f, 0.5f, 0.5f),
                inventory->getDimensionId(),
                stack);
        }
    }
}

SpecificItemDropFactory::SpecificItemDropFactory()
    : DropConfigFactory()
{}

JSONObjectValidationBuilder* SpecificItemDropFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return DropConfigFactory::addToValidator(builder)
        ->withRequiredAttribute("itemType",
            Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>(
                ItemTypeNameFactory::TYPE_ID))
        ->withRequiredAttribute("modifier",
            Game::INSTANCE->zTypeRegistry()->getValidator<ItemModifier>(),
            false,
            true)
        ->withRequiredNumber("amount")
        ->whichIsGreaterThen(0.0)
        ->withDefault(1.0)
        ->finishNumber();
}

const char* SpecificItemDropFactory::getTypeToken() const
{
    return "specificItem";
}

SpecificItemDrop* SpecificItemDropFactory::createInstance(
    Framework::JSON::JSONObject* zJson) const
{
    return new SpecificItemDrop(
        zJson->zValue("itemType")->asString()->getString(),
        zJson->hasValue("modifier")
            ? Game::INSTANCE->zTypeRegistry()->fromJson<ItemModifier>(
                  zJson->zValue("modifier"))
            : 0,
        (int)zJson->zValue("amount")->asNumber()->getNumber());
}

void SpecificItemDropFactory::addToJson(
    Framework::JSON::JSONObject* zJson, SpecificItemDrop* zObject) const
{
    zJson->addValue("itemType",
        new Framework::JSON::JSONString(zObject->getItemTypeName()));
    if (zObject->zModifier())
    {
        zJson->addValue("modifier",
            Game::INSTANCE->zTypeRegistry()->toJson(zObject->zModifier()));
    }
    zJson->addValue(
        "amount", new Framework::JSON::JSONNumber(zObject->getAmount()));
}