#include "DropConditionOperator.h"

#include "Game.h"

DropConditionOperator::DropConditionOperator(ConditionalOperator op)
    : DropCondition(),
      op(op)
{}

DropConditionOperator::~DropConditionOperator()
{
    for (DropCondition* condition : conditions)
    {
        delete condition;
    }
}

ConditionalOperator DropConditionOperator::getOperator() const
{
    return op;
}

const Framework::Array<DropCondition*>&
DropConditionOperator::getConditions() const
{
    return conditions;
}

void DropConditionOperator::addCondition(DropCondition* condition)
{
    conditions.add(condition);
}

bool DropConditionOperator::evaluate(Entity* zActor,
    Item* zItem,
    ItemSkill* zUsedSkill,
    Framework::Either<Block*, Entity*> zDestroyedObject)
{
    switch (op)
    {
    case AND:
        {
            bool result = true;
            for (DropCondition* condition : conditions)
            {
                result &= condition->evaluate(
                    zActor, zItem, zUsedSkill, zDestroyedObject);
            }
            return result;
        }
    case OR:
        {
            bool result = false;
            for (DropCondition* condition : conditions)
            {
                result |= condition->evaluate(
                    zActor, zItem, zUsedSkill, zDestroyedObject);
            }
            return result;
        }
    }
    return false;
}

DropConditionNegation::DropConditionNegation(DropCondition* condition)
    : DropCondition(),
      condition(condition)
{}

DropConditionNegation::~DropConditionNegation()
{
    delete condition;
}

const DropCondition* DropConditionNegation::zCondition() const
{
    return condition;
}

bool DropConditionNegation::evaluate(Entity* zActor,
    Item* zItem,
    ItemSkill* zUsedSkill,
    Framework::Either<Block*, Entity*> zDestroyedObject)
{
    return !condition->evaluate(zActor, zItem, zUsedSkill, zDestroyedObject);
}

DropAllwaysCondition::DropAllwaysCondition()
    : DropCondition()
{}

bool DropAllwaysCondition::evaluate(Entity* zActor,
    Item* zItem,
    ItemSkill* zUsedSkill,
    Framework::Either<Block*, Entity*> zDestroyedObject)
{
    return true;
}

DropConditionOperatorFactory::DropConditionOperatorFactory()
    : SubTypeFactory()
{}

JSONObjectValidationBuilder* DropConditionOperatorFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return builder->withRequiredString("operator")
        ->whichIsOneOf({"AND", "OR"})
        ->finishString()
        ->withRequiredAttribute("conditions",
            Framework::Validator::DataValidator::buildForArray()
                ->addAcceptedTypeInArray(Game::INSTANCE->zTypeRegistry()
                        ->getValidator<DropCondition>())
                ->finishArray());
}

const char* DropConditionOperatorFactory::getTypeToken() const
{
    return "operator";
}

DropConditionOperator* DropConditionOperatorFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    DropConditionOperator* result = new DropConditionOperator(
        zJson->zValue("operator")->asString()->getString().istGleich("AND")
            ? AND
            : OR);
    for (Framework::JSON::JSONValue* value :
        *zJson->zValue("conditions")->asArray())
    {
        result->addCondition(
            Game::INSTANCE->zTypeRegistry()->fromJson<DropCondition>(value));
    }
    return result;
}

Framework::JSON::JSONObject* DropConditionOperatorFactory::toJsonObject(
    DropConditionOperator* zObject) const
{
    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
    switch (zObject->getOperator())
    {
    case AND:
        result->addValue("operator", new Framework::JSON::JSONString("AND"));
        break;
    case OR:
        result->addValue("operator", new Framework::JSON::JSONString("OR"));
        break;
    }
    Framework::JSON::JSONArray* array = new Framework::JSON::JSONArray();
    for (DropCondition* condition : zObject->getConditions())
    {
        array->addValue(Game::INSTANCE->zTypeRegistry()->toJson(condition));
    }
    result->addValue("conditions", array);
    return nullptr;
}

DropConditionNegationFactory::DropConditionNegationFactory()
    : SubTypeFactory()
{}

JSONObjectValidationBuilder* DropConditionNegationFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return builder->withRequiredAttribute("condition",
        Game::INSTANCE->zTypeRegistry()->getValidator<DropCondition>());
}

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

DropConditionNegation* DropConditionNegationFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    return new DropConditionNegation(
        Game::INSTANCE->zTypeRegistry()->fromJson<DropCondition>(
            zJson->zValue("condition")));
}

Framework::JSON::JSONObject* DropConditionNegationFactory::toJsonObject(
    DropConditionNegation* zObject) const
{
    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
    result->addValue("condition",
        Game::INSTANCE->zTypeRegistry()->toJson(zObject->zCondition()));
    return result;
}

DropAllwaysConditionFactory::DropAllwaysConditionFactory()
    : SubTypeFactory()
{}

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

const char* DropAllwaysConditionFactory::getTypeToken() const
{
    return "allways";
}

DropAllwaysCondition* DropAllwaysConditionFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    return new DropAllwaysCondition();
}

Framework::JSON::JSONObject* DropAllwaysConditionFactory::toJsonObject(
    DropAllwaysCondition* zObject) const
{
    return new Framework::JSON::JSONObject();
}