#include "QuestReward.h"

#include <Fenster.h>

#include "Entity.h"
#include "Game.h"
#include "Player.h"
#include "Quest.h"

QuestReward::QuestReward()
    : ReferenceCounter()
{}

bool QuestReward::validateSettings(
    Framework::XML::Element* zParent, QuestStorage* zStorage)
{
    return true;
}

void QuestReward::api(Framework::StreamReader* message,
    Framework::XML::Element* zParent,
    QuestStorage* zStorage)
{}

void QuestReward::setRewardId(Framework::Text rewardId)
{
    this->rewardId = rewardId;
}

const Framework::Text& QuestReward::getRewardId() const
{
    return rewardId;
}

ItemStackInfo::ItemStackInfo()
    : ReferenceCounter(),
      item(0),
      count(0)
{}

ItemStackInfo::~ItemStackInfo()
{
    if (item) item->release();
}

void ItemStackInfo::setItem(Item* item)
{
    if (this->item) this->item->release();
    this->item = item;
}

Item* ItemStackInfo::zItem() const
{
    return item;
}

void ItemStackInfo::setCount(int count)
{
    this->count = count;
}

int ItemStackInfo::getCount() const
{
    return count;
}

ItemStackInfoType::ItemStackInfoType()
    : ObjectTypeFactory<ItemStackInfo>()
{}

ItemStackInfo* ItemStackInfoType::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    ItemStackInfo* result = new ItemStackInfo();
    result->setItem(Game::INSTANCE->zTypeRegistry()->fromJson<Item>(
        zJson->asObject()->zValue("item")));
    result->setCount(
        (int)zJson->asObject()->zValue("count")->asNumber()->getNumber());
    return result;
}

Framework::JSON::JSONObject* ItemStackInfoType::toJsonObject(
    ItemStackInfo* zObject) const
{
    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
    result->addValue(
        "item", Game::INSTANCE->zTypeRegistry()->toJson(zObject->zItem()));
    result->addValue(
        "count", new Framework::JSON::JSONNumber((double)zObject->getCount()));
    return result;
}

JSONObjectValidationBuilder* ItemStackInfoType::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return builder
        ->withRequiredAttribute(
            "item", Game::INSTANCE->zTypeRegistry()->getValidator<Item>())
        ->withRequiredNumber("count")
        ->withDefault(1.0)
        ->whichIsGreaterOrEqual(1.0)
        ->finishNumber();
}

QuestRewardGiveItems::QuestRewardGiveItems()
    : QuestReward()
{}

void QuestRewardGiveItems::giveReward(Framework::XML::Element* zParent,
    QuestStorage* zStorage,
    Entity* zTargetEntity)
{
    Player* p = dynamic_cast<Player*>(zTargetEntity);
    if (p)
    {
        zStorage->putValue(
            (Framework::Text("reward_") += rewardId) += "_given_to",
            new Framework::JSON::JSONString(p->getName()));
    }
    for (ItemStackInfo* info : items)
    {
        ItemStack* stack = new ItemStack(
            dynamic_cast<Item*>(info->zItem()->getThis()), info->getCount());
        zTargetEntity->unsaveAddItem(stack, Direction::NO_DIRECTION, 0);
        if (stack->getSize() > 0)
        {
            Game::INSTANCE->spawnItem(zTargetEntity->getLocation(),
                zTargetEntity->getDimensionId(),
                stack);
        }
        else
        {
            stack->release();
        }
    }
}

void QuestRewardGiveItems::addRewardUIML(Framework::XML::Element* zParent,
    QuestStorage* zStorage,
    Framework::Text onClickPrefix)
{
    Framework::XML::Element* container
        = new Framework::XML::Element("<frame width=\"100%\" height=\"auto\" "
                                      "display=\"column\" gap=\"10\"/>");
    container->setAttribute("style",
        Framework::Text() += (Framework::Fenster::Style::Sichtbar
                              | Framework::Fenster::Style::Erlaubt));
    container->setAttribute("id", Framework::Text("reward_") += rewardId);
    container->addChild(new Framework::XML::Element(
        "<text width=\"auto\" height=\"auto\">Item Reward:</text>"));
    for (ItemStackInfo* info : items)
    {
        auto stack = new Framework::XML::Element(
            "<itemStack width=\"50\" height=\"50\"/>");
        stack->setAttribute(
            "id", (Framework::Text("reward_") += rewardId) += "_item_stack");
        stack->setAttribute("count", info->getCount());
        stack->setAttribute("type", info->zItem()->getTypeId());
        stack->addChild(
            new Framework::XML::Element(info->zItem()->getTooltipUIML()));
        auto text = new Framework::XML::Element(
            "<text margin-left=\"10\" width=\"auto\" height=\"auto\"/>");
        text->setAttribute("id",
            (Framework::Text("reward_") += rewardId) += "_item_description");
        text->setText((Framework::Text(info->getCount()) += " ")
                      += info->zItem()->getName());
        text->setAttribute("align-left", stack->getAttributeValue("id"));
        text->setAttribute("align-y", stack->getAttributeValue("id"));
        container->addChild(stack);
        container->addChild(text);
        if (zStorage->containsKey(
                (Framework::Text("reward_") += rewardId) += "_given_to"))
        {
            auto givenTo = new Framework::XML::Element(
                "<text width=\"auto\" height=\"auto\" margin-top=\"10\" "
                "text-color=\"0xFF00FF00\"/>");
            auto name = zStorage
                            ->zValue((Framework::Text("reward_") += rewardId)
                                     += "_given_to")
                            ->asString();
            givenTo->setText(
                (Framework::Text("Given to: ") += name->getString()));
            givenTo->setAttribute("align-top", text->getAttributeValue("id"));
            givenTo->setAttribute("align-x", text->getAttributeValue("id"));
            container->addChild(givenTo);
        }
    }
    zParent->addChild(container);
}

QuestRewardGiveItemsType::QuestRewardGiveItemsType()
    : QuestRewardFactoryBase()
{}

QuestRewardGiveItems* QuestRewardGiveItemsType::createValue(
    Framework::JSON::JSONObject* zJson) const
{
    return new QuestRewardGiveItems();
}

QuestRewardGiveItems* QuestRewardGiveItemsType::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    QuestRewardGiveItems* result = QuestRewardFactoryBase::fromJson(zJson);
    Framework::JSON::JSONArray* itemsJson = zJson->zValue("items")->asArray();
    for (Framework::JSON::JSONValue* itemJson : *itemsJson)
    {
        result->items.add(
            Game::INSTANCE->zTypeRegistry()->fromJson<ItemStackInfo>(itemJson));
    }
    return result;
}

Framework::JSON::JSONObject* QuestRewardGiveItemsType::toJsonObject(
    QuestRewardGiveItems* zObject) const
{
    Framework::JSON::JSONObject* zResult
        = QuestRewardFactoryBase::toJsonObject(zObject);
    zResult->addValue(
        "rewardId", new Framework::JSON::JSONString(zObject->getRewardId()));
    Framework::JSON::JSONArray* itemsJson = new Framework::JSON::JSONArray();
    for (ItemStackInfo* item : zObject->items)
    {
        itemsJson->addValue(Game::INSTANCE->zTypeRegistry()->toJson(item));
    }
    zResult->addValue("items", itemsJson);
    return zResult;
}

JSONObjectValidationBuilder* QuestRewardGiveItemsType::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return QuestRewardFactoryBase::addToValidator(
        builder->withRequiredArray("items")
            ->addAcceptedTypeInArray(
                Game::INSTANCE->zTypeRegistry()->getValidator<ItemStackInfo>())
            ->finishArray());
}

const char* QuestRewardGiveItemsType::getTypeToken() const
{
    return "give_items";
}