#include "BlockFilter.h"

#include "Block.h"
#include "FluidBlock.h"
#include "Game.h"

BlockFilter::BlockFilter()
    : ReferenceCounter()
{}

BlockFilterAnd::BlockFilterAnd()
    : BlockFilter()
{}

bool BlockFilterAnd::test(const Block* zBlock)
{
    for (BlockFilter* filter : filters)
    {
        if (!filter->test(zBlock))
        {
            return false;
        }
    }
    return true;
}

void BlockFilterAnd::addFilter(BlockFilter* filter)
{
    filters.add(filter);
}

Framework::RCArray<BlockFilter> BlockFilterAnd::getFilters() const
{
    return filters;
}

BlockFilterAndFactory::BlockFilterAndFactory()
    : SubTypeFactory()
{}

BlockFilterAnd* BlockFilterAndFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    BlockFilterAnd* result = new BlockFilterAnd();
    for (Framework::JSON::JSONValue* value :
        *zJson->zValue("filters")->asArray())
    {
        result->addFilter(
            Game::INSTANCE->zTypeRegistry()->fromJson<BlockFilter>(value));
    }
    return result;
}

Framework::JSON::JSONObject* BlockFilterAndFactory::toJsonObject(
    BlockFilterAnd* zObject) const
{
    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
    Framework::JSON::JSONArray* filters = new Framework::JSON::JSONArray();
    for (BlockFilter* filter : zObject->getFilters())
    {
        filters->addValue(Game::INSTANCE->zTypeRegistry()->toJson(filter));
    }
    result->addValue("filters", filters);
    return result;
}

JSONObjectValidationBuilder* BlockFilterAndFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return builder->withRequiredArray("filters")
        ->addAcceptedTypeInArray(
            Game::INSTANCE->zTypeRegistry()->getValidator<BlockFilter>())
        ->finishArray();
}

const char* BlockFilterAndFactory::getTypeToken() const
{
    return "and";
}

BlockFilterOr::BlockFilterOr()
    : BlockFilter()
{}

bool BlockFilterOr::test(const Block* zBlock)
{
    for (BlockFilter* filter : filters)
    {
        if (filter->test(zBlock))
        {
            return true;
        }
    }
    return false;
}

void BlockFilterOr::addFilter(BlockFilter* filter)
{
    filters.add(filter);
}

Framework::RCArray<BlockFilter> BlockFilterOr::getFilters() const
{
    return filters;
}

BlockFilterOrFactory::BlockFilterOrFactory()
    : SubTypeFactory()
{}

BlockFilterOr* BlockFilterOrFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    BlockFilterOr* result = new BlockFilterOr();
    for (Framework::JSON::JSONValue* value :
        *zJson->zValue("filters")->asArray())
    {
        result->addFilter(
            Game::INSTANCE->zTypeRegistry()->fromJson<BlockFilter>(value));
    }
    return result;
}

Framework::JSON::JSONObject* BlockFilterOrFactory::toJsonObject(
    BlockFilterOr* zObject) const
{
    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
    Framework::JSON::JSONArray* filters = new Framework::JSON::JSONArray();
    for (BlockFilter* filter : zObject->getFilters())
    {
        filters->addValue(Game::INSTANCE->zTypeRegistry()->toJson(filter));
    }
    result->addValue("filters", filters);
    return result;
}

JSONObjectValidationBuilder* BlockFilterOrFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return builder->withRequiredArray("filters")
        ->addAcceptedTypeInArray(
            Game::INSTANCE->zTypeRegistry()->getValidator<BlockFilter>())
        ->finishArray();
}

const char* BlockFilterOrFactory::getTypeToken() const
{
    return "or";
}

BlockFilterNot::BlockFilterNot()
    : BlockFilter(),
      filter(0)
{}

BlockFilterNot::~BlockFilterNot()
{
    if (filter) filter->release();
}

bool BlockFilterNot::test(const Block* zBlock)
{
    return !filter->test(zBlock);
}

void BlockFilterNot::setFilter(BlockFilter* filter)
{
    if (this->filter) this->filter->release();
    this->filter = filter;
}

BlockFilter* BlockFilterNot::zFilter() const
{
    return filter;
}

BlockFilterNotFactory::BlockFilterNotFactory()
    : SubTypeFactory()
{}

BlockFilterNot* BlockFilterNotFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    BlockFilterNot* result = new BlockFilterNot();
    result->setFilter(Game::INSTANCE->zTypeRegistry()->fromJson<BlockFilter>(
        zJson->zValue("filter")));
    return result;
}

Framework::JSON::JSONObject* BlockFilterNotFactory::toJsonObject(
    BlockFilterNot* zObject) const
{
    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
    result->addValue(
        "filter", Game::INSTANCE->zTypeRegistry()->toJson(zObject->zFilter()));
    return result;
}

JSONObjectValidationBuilder* BlockFilterNotFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return builder->withRequiredAttribute(
        "filter", Game::INSTANCE->zTypeRegistry()->getValidator<BlockFilter>());
}

const char* BlockFilterNotFactory::getTypeToken() const
{
    return "not";
}

BlockFilterBlockType::BlockFilterBlockType()
    : BlockFilter()
{}

bool BlockFilterBlockType::test(const Block* zBlock)
{
    for (int blockTypeId : blockTypeIds)
    {
        if (zBlock->zBlockType()->getId() == blockTypeId)
        {
            return true;
        }
    }
    return false;
}

void BlockFilterBlockType::addBlockTypeId(int blockTypeId)
{
    blockTypeIds.add(blockTypeId);
}

const Framework::Array<int>& BlockFilterBlockType::getBlockTypeIds() const
{
    return blockTypeIds;
}

BlockFilterBlockTypeFactory::BlockFilterBlockTypeFactory()
    : SubTypeFactory()
{}

BlockFilterBlockType* BlockFilterBlockTypeFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    BlockFilterBlockType* result = new BlockFilterBlockType();
    for (Framework::JSON::JSONValue* value :
        *zJson->zValue("typeNames")->asArray())
    {
        result->addBlockTypeId(
            Game::INSTANCE->getBlockTypeId(value->asString()->getString()));
    }
    return result;
}

Framework::JSON::JSONObject* BlockFilterBlockTypeFactory::toJsonObject(
    BlockFilterBlockType* zObject) const
{
    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
    Framework::JSON::JSONArray* typeNames = new Framework::JSON::JSONArray();
    for (int typeId : zObject->getBlockTypeIds())
    {
        typeNames->addValue(new Framework::JSON::JSONString(
            Game::INSTANCE->zBlockType(typeId)->getName()));
    }
    result->addValue("typeNames", typeNames);
    return result;
}

JSONObjectValidationBuilder* BlockFilterBlockTypeFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return builder->withRequiredArray("typeNames")
        ->addAcceptedTypeInArray(
            Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>(
                BlockTypeNameFactory::TYPE_ID))
        ->finishArray();
}

const char* BlockFilterBlockTypeFactory::getTypeToken() const
{
    return "types";
}

BlockFilterTypeGroup::BlockFilterTypeGroup()
    : BlockFilter()
{}

bool BlockFilterTypeGroup::test(const Block* zBlock)
{
    for (Framework::Text* groupName : groupNames)
    {
        for (Framework::Text* otherGroupName :
            zBlock->zBlockType()->getGroupNames())
        {
            if (groupName->istGleich(*otherGroupName))
            {
                return true;
            }
        }
    }
    return false;
}

void BlockFilterTypeGroup::addGroupName(const Framework::Text groupName)
{
    groupNames.add(new Framework::Text(groupName));
}

const Framework::RCArray<Framework::Text>&
BlockFilterTypeGroup::getGroupNames() const
{
    return groupNames;
}

BlockFilterTypeGroupFactory::BlockFilterTypeGroupFactory()
    : SubTypeFactory()
{}

BlockFilterTypeGroup* BlockFilterTypeGroupFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    BlockFilterTypeGroup* result = new BlockFilterTypeGroup();
    for (Framework::JSON::JSONValue* value :
        *zJson->zValue("groupNames")->asArray())
    {
        result->addGroupName(value->asString()->getString());
    }
    return result;
}

Framework::JSON::JSONObject* BlockFilterTypeGroupFactory::toJsonObject(
    BlockFilterTypeGroup* zObject) const
{
    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
    Framework::JSON::JSONArray* groupNames = new Framework::JSON::JSONArray();
    for (Framework::Text* groupName : zObject->getGroupNames())
    {
        groupNames->addValue(new Framework::JSON::JSONString(*groupName));
    }
    result->addValue("groupNames", groupNames);
    return result;
}

JSONObjectValidationBuilder* BlockFilterTypeGroupFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return builder->withRequiredArray("groupNames")
        ->addAcceptedStringInArray()
        ->finishString()
        ->finishArray();
}

const char* BlockFilterTypeGroupFactory::getTypeToken() const
{
    return "groups";
}

BlockFilterMaxHardness::BlockFilterMaxHardness()
    : BlockFilter()
{}

bool BlockFilterMaxHardness::test(const Block* zBlock)
{
    return zBlock->zBlockType()->getHardness() <= maxHardness;
}

void BlockFilterMaxHardness::setMaxHardness(float maxHardness)
{
    this->maxHardness = maxHardness;
}

float BlockFilterMaxHardness::getMaxHardness() const
{
    return maxHardness;
}

BlockFilterMaxHardnessFactory::BlockFilterMaxHardnessFactory()
    : SubTypeFactory()
{}

BlockFilterMaxHardness* BlockFilterMaxHardnessFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    BlockFilterMaxHardness* result = new BlockFilterMaxHardness();
    result->setMaxHardness(
        (float)zJson->zValue("maxHardness")->asNumber()->getNumber());
    return result;
}

Framework::JSON::JSONObject* BlockFilterMaxHardnessFactory::toJsonObject(
    BlockFilterMaxHardness* zObject) const
{
    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
    result->addValue("maxHardness",
        new Framework::JSON::JSONNumber(zObject->getMaxHardness()));
    return result;
}

JSONObjectValidationBuilder* BlockFilterMaxHardnessFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return builder->withRequiredNumber("maxHardness")
        ->whichIsGreaterOrEqual(0.0)
        ->finishNumber();
}

const char* BlockFilterMaxHardnessFactory::getTypeToken() const
{
    return "maxHardness";
}

BlockFilterMinHardness::BlockFilterMinHardness()
    : BlockFilter(),
      minHardness(0.f)
{}

bool BlockFilterMinHardness::test(const Block* zBlock)
{
    return zBlock->zBlockType()->getHardness() >= minHardness;
}

void BlockFilterMinHardness::setMinHardness(float minHardness)
{
    this->minHardness = minHardness;
}

float BlockFilterMinHardness::getMinHardness() const
{
    return minHardness;
}

BlockFilterMinHardnessFactory::BlockFilterMinHardnessFactory()
    : SubTypeFactory()
{}

BlockFilterMinHardness* BlockFilterMinHardnessFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    BlockFilterMinHardness* result = new BlockFilterMinHardness();
    result->setMinHardness(
        (float)zJson->zValue("minHardness")->asNumber()->getNumber());
    return result;
}

Framework::JSON::JSONObject* BlockFilterMinHardnessFactory::toJsonObject(
    BlockFilterMinHardness* zObject) const
{
    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
    result->addValue("minHardness",
        new Framework::JSON::JSONNumber(zObject->getMinHardness()));
    return result;
}

JSONObjectValidationBuilder* BlockFilterMinHardnessFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return builder->withRequiredNumber("minHardness")
        ->whichIsGreaterOrEqual(0.0)
        ->finishNumber();
}

const char* BlockFilterMinHardnessFactory::getTypeToken() const
{
    return "minHardness";
}

BlockFilterMaxHeat::BlockFilterMaxHeat()
    : BlockFilter(),
      maxHeat(0.f)
{}

bool BlockFilterMaxHeat::test(const Block* zBlock)
{
    const FluidBlockType* type
        = dynamic_cast<const FluidBlockType*>(zBlock->zBlockType());
    return type && type->getHeat() <= maxHeat;
}

void BlockFilterMaxHeat::setMaxHeat(float maxHeat)
{
    this->maxHeat = maxHeat;
}

float BlockFilterMaxHeat::getMaxHeat() const
{
    return maxHeat;
}

BlockFilterMaxHeatFactory::BlockFilterMaxHeatFactory()
    : SubTypeFactory()
{}

BlockFilterMaxHeat* BlockFilterMaxHeatFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    BlockFilterMaxHeat* result = new BlockFilterMaxHeat();
    result->setMaxHeat((float)zJson->zValue("heat")->asNumber()->getNumber());
    return result;
}

Framework::JSON::JSONObject* BlockFilterMaxHeatFactory::toJsonObject(
    BlockFilterMaxHeat* zObject) const
{
    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
    result->addValue(
        "heat", new Framework::JSON::JSONNumber(zObject->getMaxHeat()));
    return result;
}

JSONObjectValidationBuilder* BlockFilterMaxHeatFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return builder->withRequiredNumber("heat")->finishNumber();
}

const char* BlockFilterMaxHeatFactory::getTypeToken() const
{
    return "maxHeat";
}