#include "Chest.h"

#include <TextFeld.h>

#include "Chunk.h"
#include "Dimension.h"
#include "Entity.h"
#include "Game.h"
#include "ItemSlot.h"
#include "UIController.h"
#include "UIDialog.h"

Chest::Chest(int typeId, Framework::Vec3<int> pos, int dimensionId)
    : BasicBlock(typeId, pos, dimensionId, 1),
      open(0),
      userEntityId(0)
{
    for (int i = 0; i < 30; i++)
    {
        ItemSlot* slot = new ItemSlot(
            "Inventory", 50, i, i, ANY_DIRECTION, ANY_DIRECTION, 0);
        addSlot(slot);
    }
}

void Chest::onDestroy()
{
    for (ItemSlot* slot : *this)
    {
        if (!slot->isEmpty())
        {
            Game::INSTANCE->spawnItem(
                location + Framework::Vec3<float>(0.5f, 0.5f, 0.5f),
                getDimensionId(),
                slot->takeItemsOut(slot->getNumberOfItems(), NO_DIRECTION));
        }
    }
    BasicBlock::onDestroy();
}

void Chest::onDialogClosed(Framework::Text dialogId)
{
    if (dialogId.istGleich(getDialogId()))
    {
        open = 0;
        userEntityId = 0;
        NetworkMessage* msg = new NetworkMessage();
        msg->animateBlockBone(getDimensionId(),
            Game::getChunkCenter(getPos().x, getPos().y),
            Chunk::index(Dimension::chunkCoordinates(getPos())),
            1,
            1.0,
            Framework::Vec3<float>(0.5f, 0.f, 0.45f),
            Framework::Vec3<float>(
                0.0f, 0.f, 0.f)); // close the chest over one second
        broadcastMessage(msg);
    }
}

Framework::Text Chest::getDialogId() const
{
    Framework::Text dialogId = "chest_inventory_";
    dialogId.append() << getDimensionId() << "," << getPos().x << ","
                      << getPos().y << "," << getPos().z;
    return dialogId;
}

bool Chest::onTick(TickQueue* zQueue, int numTicks, bool& blocked)
{
    if (open)
    {
        if (!Game::INSTANCE->zEntity(userEntityId))
        {
            onDialogClosed(getDialogId());
        }
    }
    return open;
}

bool Chest::interact(Item* zItem, Entity* zActor)
{
    lock();
    if (open)
    {
        if (!Game::INSTANCE->zEntity(userEntityId)) open = 0;
    }
    if (!open)
    {
        userEntityId = zActor->getId();
        open = 1;
        Framework::Text uiml = "";
        uiml.append()
            << "<dialog id=\"" << getDialogId()
            << "\" title=\"Chest\" "
               "notifyOnClose=\""
            << getDimensionId() << "," << getPos().x << "," << getPos().y << ","
            << getPos().z
            << "\" "
               "width=\"610\" "
               "height=\"480\">"
            << "<inventory id=\"chest_inventory\" margin-bottom=\"18\" "
               "align-bottom=\"player_label\" align-left=\"start\" "
               "margin-left=\"9\" width=\"592\" height=\"172\" rowSize=\"10\" "
               "numSlots=\"30\" slotNameFilter=\"\" target=\""
            << getDimensionId() << "," << getPos().x << "," << getPos().y << ","
            << getPos().z << "\"/>"
            << "<text id=\"player_label\" width=\"100%\" height=\"auto\" "
               "style=\""
            << std::uppercase << std::hex
            << (Framework::TextFeld::Style::Text
                   | Framework::TextFeld::Style::Center)
            << std::dec << std::nouppercase
            << "\" margin-bottom=\"9\" "
               "align-bottom=\"player_inventory\">Player "
               "Inventory</text>"
            << "<inventory id=\"player_inventory\" margin-bottom=\"18\" "
               "align-bottom=\"item_bar\" align-left=\"start\" "
               "margin-left=\"9\" width=\"592\" height=\"172\" rowSize=\"10\" "
               "numSlots=\"30\" slotNameFilter=\"Inventory\" target=\""
            << zActor->getId() << "\"/>"
            << "<inventory id=\"item_bar\" margin-bottom=\"9\" "
               "align-bottom=\"end\" align-left=\"start\" margin-left=\"9\" "
               "width=\"592\" height=\"52\" rowSize=\"10\" numSlots=\"10\" "
               "slotNameFilter=\"ItemBar\" target=\""
            << zActor->getId() << "\"/>"
            << "</dialog>";
        Game::INSTANCE->zUIController()->addDialog(new UIDialog(
            getDialogId(), zActor->getId(), new Framework::XML::Element(uiml)));
        NetworkMessage* msg = new NetworkMessage();
        msg->animateBlockBone(getDimensionId(),
            Game::getChunkCenter(getPos().x, getPos().y),
            Chunk::index(Dimension::chunkCoordinates(getPos())),
            1,
            1.0,
            Framework::Vec3<float>(0.5f, 0.f, 0.45f),
            Framework::Vec3<float>(
                0.0f, (float)(PI / 2.0), 0.f)); // open the chest over 1 second
        broadcastMessage(msg);
    }
    unlock();
    return false; // item was not changed
}

void Chest::sendModelInfo(NetworkMessage* zMessage)
{
    if (open)
    {
        zMessage->animateBlockBone(getDimensionId(),
            Game::getChunkCenter(getPos().x, getPos().y),
            Chunk::index(Dimension::chunkCoordinates(getPos())),
            1,
            0.0,
            Framework::Vec3<float>(0.5f, 0.f, 0.45f),
            Framework::Vec3<float>(
                0.0f, (float)(PI / 2.0), 0.f)); // open the chest instantly
    }
}

ChestBlockType::ChestBlockType()
    : BasicBlockType()
{}

Block* ChestBlockType::createBlock(
    Framework::Vec3<int> position, int dimensionId) const
{
    return new Chest(getItemTypeId(), position, dimensionId);
}

ChestBlockTypeFactory::ChestBlockTypeFactory()
    : BasicBlockTypeFactory()
{}

BasicBlockType* ChestBlockTypeFactory::createValue(
    Framework::JSON::JSONObject* zJson) const
{
    return new ChestBlockType();
}

BasicBlockType* ChestBlockTypeFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    return BasicBlockTypeFactory::fromJson(zJson);
}

Framework::JSON::JSONObject* ChestBlockTypeFactory::toJsonObject(
    BasicBlockType* zObject) const
{
    return BasicBlockTypeFactory::toJsonObject(zObject);
}

JSONObjectValidationBuilder* ChestBlockTypeFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return BasicBlockTypeFactory::addToValidator(builder);
}

const char* ChestBlockTypeFactory::getTypeToken() const
{
    return "chest";
}

const char* ChestBlockTypeFactory::getTypeName() const
{
    return typeid(ChestBlockType).name();
}