#include "RecipieIngredient.h"

#include <DateiSystem.h>
#include <ToolTip.h>
#include <XML.h>

#include "Globals.h"

RecipieIngredientElement::RecipieIngredientElement()
    : Framework::UIMLElement()
{}

LogicTree* RecipieIngredientElement::parse(Framework::XML::Element* zElement)
{
    if (zElement->getName().istGleich("anyItem"))
    {
        return new LogicTree(LogicalOperator::OR, new RequirementSet());
    }
    else if (zElement->getName().istGleich("attribute"))
    {
        AttributeOperator op = AttributeOperator::Equals;
        if (zElement->getAttributeValue("operator").istGleich("!="))
        {
            op = AttributeOperator::NotEquals;
        }
        else if (zElement->getAttributeValue("operator").istGleich(">"))
        {
            op = AttributeOperator::GreaterThan;
        }
        else if (zElement->getAttributeValue("operator").istGleich(">="))
        {
            op = AttributeOperator::GreaterThanOrEquals;
        }
        else if (zElement->getAttributeValue("operator").istGleich("<"))
        {
            op = AttributeOperator::LessThan;
        }
        else if (zElement->getAttributeValue("operator").istGleich("<="))
        {
            op = AttributeOperator::LessThanOrEquals;
        }
        Requirement* req = new Requirement(zElement->getAttributeValue("name"),
            zElement->getAttributeValue("value"),
            op);
        RequirementSet* set = new RequirementSet();
        set->addRequirement(req);
        req->release();
        return new LogicTree(LogicalOperator::OR, set);
    }
    else if (zElement->getName().istGleich("operator"))
    {
        bool result0_0 = (bool)(int)zElement->getAttributeValue("result_0_0");
        bool result0_1 = (bool)(int)zElement->getAttributeValue("result_0_1");
        bool result1_0 = (bool)(int)zElement->getAttributeValue("result_1_0");
        bool result1_1 = (bool)(int)zElement->getAttributeValue("result_1_1");
        if (!result0_0 && !result0_1 && !result1_0 && !result1_1)
        { // none match
            RequirementSet* set = new RequirementSet();
            Requirement* req
                = new Requirement("_x", "0", AttributeOperator::Equals);
            set->addRequirement(req);
            req->release();
            req = new Requirement("_x", "1", AttributeOperator::Equals);
            set->addRequirement(req);
            req->release();
            return new LogicTree(LogicalOperator::OR, set);
        }
        if (result0_0 && result0_1 && result1_0 && result1_1)
        { // any match
            return new LogicTree(LogicalOperator::OR, new RequirementSet());
        }
        auto iterator = zElement->getChilds();
        LogicTree* left = parse(iterator.val());
        if (!result0_0 && !result0_1 && result1_0 && result1_1)
        {
            return left;
        }
        if (result0_0 && result0_1 && !result1_0 && !result1_1)
        {
            left->negate();
            return left;
        }
        LogicTree* right = 0;
        iterator++;
        right = parse(iterator.val());
        if (!result0_0 && result0_1 && !result1_0 && result1_1)
        {
            left->release();
            return right;
        }
        if (result0_0 && !result0_1 && result1_0 && !result1_1)
        {
            left->release();
            right->negate();
            return right;
        }
        if (!result0_0 && !result0_1 && !result1_0 && result1_1)
        {
            LogicTree* result = new LogicTree(LogicalOperator::AND, 0);
            result->addChildren(left);
            result->addChildren(right);
            return result;
        }
        if (!result0_0 && result0_1 && result1_0 && result1_1)
        {
            LogicTree* result = new LogicTree(LogicalOperator::OR, 0);
            result->addChildren(left);
            result->addChildren(right);
            return result;
        }
        if (result0_0 && result0_1 && result1_0 && !result1_1)
        {
            left->negate();
            right->negate();
            LogicTree* result = new LogicTree(LogicalOperator::OR, 0);
            result->addChildren(left);
            result->addChildren(right);
            return result;
        }
        if (result0_0 && !result0_1 && !result1_0 && !result1_1)
        {
            left->negate();
            right->negate();
            LogicTree* result = new LogicTree(LogicalOperator::AND, 0);
            result->addChildren(left);
            result->addChildren(right);
            return result;
        }
        if (!result0_0 && !result0_1 && result1_0 && !result1_1)
        {
            right->negate();
            LogicTree* result = new LogicTree(LogicalOperator::AND, 0);
            result->addChildren(left);
            result->addChildren(right);
            return result;
        }
        if (!result0_0 && result0_1 && !result1_0 && !result1_1)
        {
            left->negate();
            LogicTree* result = new LogicTree(LogicalOperator::AND, 0);
            result->addChildren(left);
            result->addChildren(right);
            return result;
        }
        if (!result0_0 && result0_1 && result1_0 && !result1_1)
        {
            LogicTree* or = new LogicTree(LogicalOperator::OR, 0);
            or->addChildren(left);
            or->addChildren(right);
            LogicTree* notLeft = left->clone();
            notLeft->negate();
            LogicTree* notRight = right->clone();
            notRight->negate();
            LogicTree* notOr = new LogicTree(LogicalOperator::OR, 0);
            notOr->addChildren(notLeft);
            notOr->addChildren(notRight);
            LogicTree* result = new LogicTree(LogicalOperator::AND, 0);
            result->addChildren(or);
            result->addChildren(notOr);
            return result;
        }
        if (result0_0 && !result0_1 && !result1_0 && result1_1)
        {
            LogicTree*and = new LogicTree(LogicalOperator::AND, 0);
            and->addChildren(left);
            and->addChildren(right);
            LogicTree* notLeft = left->clone();
            notLeft->negate();
            LogicTree* notRight = right->clone();
            notRight->negate();
            LogicTree* notAnd = new LogicTree(LogicalOperator::AND, 0);
            notAnd->addChildren(notLeft);
            notAnd->addChildren(notRight);
            LogicTree* result = new LogicTree(LogicalOperator::OR, 0);
            result->addChildren(and);
            result->addChildren(notAnd);
            return result;
        }
        if (result0_0 && !result0_1 && result1_0 && result1_1)
        {
            right->negate();
            LogicTree* result = new LogicTree(LogicalOperator::OR, 0);
            result->addChildren(left);
            result->addChildren(right);
            return result;
        }
        if (result0_0 && result0_1 && !result1_0 && result1_1)
        {
            left->negate();
            LogicTree* result = new LogicTree(LogicalOperator::OR, 0);
            result->addChildren(left);
            result->addChildren(right);
            return result;
        }
    }
    return 0;
}

//! prüft, ob dieses UIML Element für ein bestimmtes xml Element zuständig
//! ist
bool RecipieIngredientElement::isApplicableFor(Framework::XML::Element& element)
{
    return element.getName().istGleich("ingredient");
}

//! erstellt eine neue Zeichnung zu einem gegebenen xml Element
Framework::Zeichnung* RecipieIngredientElement::parseElement(
    Framework::XML::Element& element, Framework::UIMLContainer& generalFactory)
{
    int amount = (int)element.getAttributeValue("amount");
    RecipieIngredient* result = new RecipieIngredient(amount);
    Framework::XML::Editor logicSelector = element.selectChildsByName("logic");
    if (logicSelector.exists())
    {
        LogicTree* logic = new LogicTree(LogicalOperator::OR, 0);
        logicSelector.selectChildren().forEach(
            [this, logic](Framework::XML::Element* zElement) {
                logic->addChildren(parse(zElement));
            });
        Framework::RCArray<RequirementSet>* requirements = logic->resolve();
        logic->release();
        for (RequirementSet* set : *requirements)
        {
            result->addPossibleItem(set->getIcon(),
                new Text(set->renderToTooltip()),
                set->getItemType());
        }
        requirements->release();
    }
    return result;
}

bool RecipieIngredientElement::updateElement(Framework::XML::Element& element,
    Framework::Zeichnung& z,
    Framework::UIMLContainer& generalFactory)
{
    return false;
}

//! wendet die layout parameter zu einer Zeichnung an
void RecipieIngredientElement::layout(Framework::XML::Element& element,
    Framework::Zeichnung& z,
    int pWidth,
    int pHeight,
    Framework::UIMLContainer& generalLayouter)
{
    UIMLElement::layout(element, z, pWidth, pHeight, generalLayouter);
    z.setWidth(50);
    z.setHeight(50);
}

RecipieIngredient::RecipieIngredient(int amount)
    : Framework::ZeichnungHintergrund(),
      currentIconIndex(0),
      timtUntilNextIcon(2.0),
      amount(amount)
{
    setStyle(Framework::Zeichnung::Style::Erlaubt
             | Framework::Zeichnung::Style::Sichtbar);
    Framework::ToolTip* tip = new Framework::ToolTip(window->zBildschirm());
    tip->addStyle(Framework::ZeichnungHintergrund::Style::Hintergrund
                  | Framework::ZeichnungHintergrund::Style::HAlpha
                  | Framework::ZeichnungHintergrund::Style::Rahmen
                  | Framework::ZeichnungHintergrund::Style::Sichtbar);
    tip->setHintergrundFarbe(0xA0000000);
    tip->setRahmenFarbe(0xFFFFFFFF);
    tip->setRahmenBreite(1);
    toolTip = uiFactory.createTextFeld(uiFactory.initParam);
    toolTip->setText("");
    toolTip->setSize(0, 0);
    toolTip->addStyle(Framework::TextFeld::Style::Mehrzeilig);
    tip->addMember(toolTip);
    tip->setWarten(0.5);
    setToolTipZ(tip);
}

void RecipieIngredient::addPossibleItem(
    Framework::Bild* icon, Framework::Text* toolTip, int typeId)
{
    icons.add(icon);
    toolTips.add(toolTip);
    itemTypes.add(typeId);
    if (toolTips.getEintragAnzahl() == 1)
    {
        this->toolTip->setText(toolTips.z(0)->getText());
        this->toolTip->setSize(
            this->toolTip->getNeededWidth(), this->toolTip->getNeededHeight());
    }
}

bool RecipieIngredient::tick(double tickVal)
{
    if (!zToolTip()->isVisible())
    {
        timtUntilNextIcon -= tickVal;
        if (timtUntilNextIcon <= 0)
        {
            timtUntilNextIcon = 2.0;
            currentIconIndex++;
            if (currentIconIndex >= icons.getEintragAnzahl())
            {
                currentIconIndex = 0;
            }
            if (toolTips.getEintragAnzahl() > 0)
            {
                toolTip->setText(toolTips.z(currentIconIndex)->getText());
                toolTip->setSize(
                    toolTip->getNeededWidth(), toolTip->getNeededHeight());
                rend = 1;
            }
        }
    }
    return ZeichnungHintergrund::tick(tickVal);
}

void RecipieIngredient::render(Framework::Bild& rObj)
{
    ZeichnungHintergrund::render(rObj);
    if (!rObj.setDrawOptions(pos.x, pos.y, gr.x, gr.y)) return;
    TextRenderer tr;
    tr.setSchriftZ(
        dynamic_cast<Schrift*>(uiFactory.initParam.schrift->getThis()));
    tr.setSchriftSize(12);
    rObj.fillRegion(0, 0, 50, 50, 0xFF222222);
    if (icons.getEintragAnzahl() > 0)
        rObj.alphaBild(0, 0, 50, 50, *icons.z(currentIconIndex));
    const char* units[] = {"", "K", "M", "G", "T", "P"};
    int i = 0;
    int tmpCount = amount;
    for (; i < 6 && tmpCount > 1024; i++)
        tmpCount = tmpCount / 1024;
    Text count = tmpCount;
    count += units[i];
    tr.renderText(45 - tr.getTextBreite(count),
        45 - tr.getTextHeight(count),
        count,
        rObj,
        0xFFFFFFFF);
    rObj.releaseDrawOptions();
}

void RecipieIngredient::doMausEreignis(
    Framework::MausEreignis& me, bool userRet)
{
    if (me.id == ME_RLinks)
    {
        World::INSTANCE->zClient()->craftingUIMLRequest(
            itemTypes.get(currentIconIndex));
    }
    ZeichnungHintergrund::doMausEreignis(me, userRet);
}

Requirement::Requirement(
    Framework::Text attribute, Framework::Text value, AttributeOperator op)
    : Framework::ReferenceCounter(),
      attribute(attribute),
      value(value),
      op(op)
{}

void Requirement::negate()
{
    switch (op)
    {
    case AttributeOperator::Equals:
        op = AttributeOperator::NotEquals;
        break;
    case AttributeOperator::NotEquals:
        op = AttributeOperator::Equals;
        break;
    case AttributeOperator::GreaterThan:
        op = AttributeOperator::LessThanOrEquals;
        break;
    case AttributeOperator::GreaterThanOrEquals:
        op = AttributeOperator::LessThan;
        break;
    case AttributeOperator::LessThan:
        op = AttributeOperator::GreaterThanOrEquals;
        break;
    case AttributeOperator::LessThanOrEquals:
        op = AttributeOperator::GreaterThan;
        break;
    }
}

Requirement* Requirement::clone()
{
    return new Requirement(attribute, value, op);
}

bool Requirement::contradicts(Requirement* zOther)
{
    if (zOther->attribute.istGleich(attribute.getText()))
    {
        if (zOther->value.istGleich(value))
        {
            if (op == AttributeOperator::Equals
                && zOther->op == AttributeOperator::NotEquals)
            {
                return 1;
            }
            if (op == AttributeOperator::NotEquals
                && zOther->op == AttributeOperator::Equals)
            {
                return 1;
            }
            if (op == AttributeOperator::GreaterThan
                && zOther->op == AttributeOperator::LessThan)
            {
                return 1;
            }
            if (op == AttributeOperator::LessThan
                && zOther->op == AttributeOperator::GreaterThan)
            {
                return 1;
            }
        }
        if (op == AttributeOperator::LessThan
            || op == AttributeOperator::LessThanOrEquals
            || op == AttributeOperator::GreaterThan
            || op == AttributeOperator::GreaterThanOrEquals)
        {
            double v1 = (double)value;
            double v2 = (double)zOther->value;
            if (op == AttributeOperator::LessThan
                || op == AttributeOperator::LessThanOrEquals)
            {
                if (v1 < v2)
                {
                    if (zOther->op == AttributeOperator::GreaterThan
                        || zOther->op == AttributeOperator::GreaterThanOrEquals
                        || zOther->op == AttributeOperator::Equals)
                    {
                        return 1;
                    }
                }
            }
            else if (op == AttributeOperator::GreaterThan
                     || op == AttributeOperator::GreaterThanOrEquals)
            {
                if (v1 > v2)
                {
                    if (zOther->op == AttributeOperator::LessThan
                        || zOther->op == AttributeOperator::LessThanOrEquals
                        || zOther->op == AttributeOperator::Equals)
                    {
                        return 1;
                    }
                }
            }
        }
        if (zOther->op == AttributeOperator::LessThan
            || zOther->op == AttributeOperator::LessThanOrEquals
            || zOther->op == AttributeOperator::GreaterThan
            || zOther->op == AttributeOperator::GreaterThanOrEquals)
        {
            double v1 = (double)zOther->value;
            double v2 = (double)value;
            if (zOther->op == AttributeOperator::LessThan
                || zOther->op == AttributeOperator::LessThanOrEquals)
            {
                if (v1 < v2)
                {
                    if (op == AttributeOperator::GreaterThan
                        || op == AttributeOperator::GreaterThanOrEquals
                        || op == AttributeOperator::Equals)
                    {
                        return 1;
                    }
                }
            }
            else if (zOther->op == AttributeOperator::GreaterThan
                     || zOther->op == AttributeOperator::GreaterThanOrEquals)
            {
                if (v1 > v2)
                {
                    if (op == AttributeOperator::LessThan
                        || op == AttributeOperator::LessThanOrEquals
                        || op == AttributeOperator::Equals)
                    {
                        return 1;
                    }
                }
            }
        }
    }
    return 0;
}

bool Requirement::merge(Requirement* zOther)
{
    if (zOther->attribute.istGleich(attribute.getText()))
    {
        if (zOther->value.istGleich(value))
        {
            if (op == zOther->op)
            {
                return 1;
            }
        }
        else
        {
            if (op == AttributeOperator::Equals
                && zOther->op == AttributeOperator::NotEquals)
            {
                return 1;
            }
            if (op == AttributeOperator::NotEquals
                && zOther->op == AttributeOperator::Equals)
            {
                op = AttributeOperator::Equals;
                value = zOther->value;
                return 1;
            }
        }
        if (op == AttributeOperator::LessThan
            || op == AttributeOperator::LessThanOrEquals
            || op == AttributeOperator::GreaterThan
            || op == AttributeOperator::GreaterThanOrEquals)
        {
            double v1 = (double)value;
            double v2 = (double)zOther->value;
            if (op == AttributeOperator::LessThan
                || op == AttributeOperator::LessThanOrEquals)
            {
                if (zOther->op == AttributeOperator::LessThan
                    || zOther->op == AttributeOperator::LessThanOrEquals)
                {
                    if (v1 > v2
                        || (v1 == v2
                            && op == AttributeOperator::LessThanOrEquals))
                    {
                        op = zOther->op;
                        value = zOther->value;
                    }
                    return 1;
                }
                if (zOther->op == AttributeOperator::Equals)
                {
                    if (v2 < v1
                        || (v2 == v1
                            && op == AttributeOperator::LessThanOrEquals))
                    {
                        op = zOther->op;
                        value = zOther->value;
                        return 1;
                    }
                }
                if (zOther->op == AttributeOperator::NotEquals)
                {
                    if (v2 > v1
                        || (v2 == v1 && op == AttributeOperator::LessThan))
                    {
                        return 1;
                    }
                }
            }
            else if (op == AttributeOperator::GreaterThan
                     || op == AttributeOperator::GreaterThanOrEquals)
            {
                if (zOther->op == AttributeOperator::GreaterThan
                    || zOther->op == AttributeOperator::GreaterThanOrEquals)
                {
                    if (v1 < v2
                        || (v1 == v2
                            && op == AttributeOperator::GreaterThanOrEquals))
                    {
                        op = zOther->op;
                        value = zOther->value;
                    }
                    return 1;
                }
                if (zOther->op == AttributeOperator::Equals)
                {
                    if (v2 > v1
                        || (v2 == v1
                            && op == AttributeOperator::GreaterThanOrEquals))
                    {
                        op = zOther->op;
                        value = zOther->value;
                        return 1;
                    }
                }
                if (zOther->op == AttributeOperator::NotEquals)
                {
                    if (v2 < v1
                        || (v2 == v1 && op == AttributeOperator::GreaterThan))
                    {
                        return 1;
                    }
                }
            }
        }
        if (zOther->op == AttributeOperator::LessThan
            || zOther->op == AttributeOperator::LessThanOrEquals
            || zOther->op == AttributeOperator::GreaterThan
            || zOther->op == AttributeOperator::GreaterThanOrEquals)
        {
            double v1 = (double)zOther->value;
            double v2 = (double)value;
            if (zOther->op == AttributeOperator::LessThan
                || zOther->op == AttributeOperator::LessThanOrEquals)
            {
                if (op == AttributeOperator::Equals)
                {
                    if (v2 < v1
                        || (v2 == v1
                            && zOther->op
                                   == AttributeOperator::LessThanOrEquals))
                    {
                        return 1;
                    }
                }
                if (op == AttributeOperator::NotEquals)
                {
                    if (v2 > v1
                        || (v2 == v1
                            && zOther->op == AttributeOperator::LessThan))
                    {
                        op = zOther->op;
                        value = zOther->value;
                        return 1;
                    }
                }
            }
            else if (zOther->op == AttributeOperator::GreaterThan
                     || zOther->op == AttributeOperator::GreaterThanOrEquals)
            {
                if (op == AttributeOperator::Equals)
                {
                    if (v2 > v1
                        || (v2 == v1
                            && zOther->op
                                   == AttributeOperator::GreaterThanOrEquals))
                    {
                        return 1;
                    }
                }
                if (op == AttributeOperator::NotEquals)
                {
                    if (v2 < v1
                        || (v2 == v1
                            && zOther->op == AttributeOperator::GreaterThan))
                    {
                        op = zOther->op;
                        value = zOther->value;
                        return 1;
                    }
                }
            }
        }
    }
    return 0;
}

int Requirement::getItemType()
{
    if (attribute.istGleich("Type") && op == AttributeOperator::Equals)
    {
        return (int)value;
    }
    return -1;
}

Framework::Text Requirement::getDescription()
{
    Framework::Text result = attribute;
    result += " ";
    switch (op)
    {
    case AttributeOperator::Equals:
        result += "is";
        break;
    case AttributeOperator::NotEquals:
        result += "is not";
        break;
    case AttributeOperator::LessThan:
        result += "is less than";
        break;
    case AttributeOperator::LessThanOrEquals:
        result += "is less than or equal to";
        break;
    case AttributeOperator::GreaterThan:
        result += "is greater than";
        break;
    case AttributeOperator::GreaterThanOrEquals:
        result += "is greater than or equal to";
        break;
    }
    result += " ";
    result += value;
    return result;
}

RequirementSet::RequirementSet()
    : Framework::ReferenceCounter()
{}

void RequirementSet::addRequirement(Requirement* zReq)
{
    for (Requirement* req : requirements)
    {
        if (req->merge(zReq)) return;
    }
    requirements.add(zReq->clone());
}

RequirementSet* RequirementSet::clone()
{
    RequirementSet* result = new RequirementSet();
    for (Requirement* req : requirements)
    {
        result->requirements.add(req->clone());
    }
    return result;
}

void RequirementSet::addRequirements(RequirementSet* zOther)
{
    for (Requirement* req : zOther->requirements)
    {
        addRequirement(req);
    }
}

void RequirementSet::negate(LogicTree* zNode)
{
    for (Requirement* req : requirements)
    {
        Requirement* negate = req->clone();
        negate->negate();
        RequirementSet* set = new RequirementSet();
        set->addRequirement(negate);
        negate->release();
        zNode->addChildren(new LogicTree(LogicalOperator::OR, set));
    }
}

bool RequirementSet::isNoneMatch() const
{
    for (Requirement* req : requirements)
    {
        for (Requirement* req2 : requirements)
        {
            if (req != req2)
            {
                if (req->contradicts(req2))
                {
                    return 1;
                }
            }
        }
    }
    return 0;
}

bool RequirementSet::isAnyMatch() const
{
    return requirements.getEintragAnzahl() == 0;
}

Framework::Text RequirementSet::renderToTooltip() const
{
    int itemType = -1;
    for (Requirement* req : requirements)
    {
        int rT = req->getItemType();
        if (rT != -1)
        {
            if (itemType == -1)
            {
                itemType = rT;
            }
            else if (itemType != rT)
            {
                itemType = -2;
            }
        }
    }
    Framework::Text result = "";
    if (itemType == -2)
    {
        return "No Item matches this filter";
    }
    else if (itemType == -1)
    {
        result += "Any Item";
    }
    else
    {
        result += zItemType(itemType)->getName();
    }
    if (requirements.getEintragAnzahl() > 0)
    {
        bool first = 1;
        for (Requirement* req : requirements)
        {
            if (req->getItemType() == -1)
            {
                if (first)
                {
                    result += ":\n";
                    first = 0;
                }
                result += "  ";
                result += req->getDescription();
                result += "\n";
            }
        }
    }
    return result;
}

Framework::Bild* RequirementSet::getIcon() const
{
    int itemType = -1;
    for (Requirement* req : requirements)
    {
        int rT = req->getItemType();
        if (rT != -1)
        {
            if (itemType == -1)
            {
                itemType = rT;
            }
            else if (itemType != rT)
            {
                itemType = -2;
            }
        }
    }
    if (itemType == -2)
    {
        LTDBDatei dat;
        dat.setDatei(new Text("data/images/gui_icons.ltdb"));
        dat.leseDaten(0);
        return dat.laden(0, new Text("noitem.png"));
    }
    else if (itemType == -1)
    {
        LTDBDatei dat;
        dat.setDatei(new Text("data/images/gui_icons.ltdb"));
        dat.leseDaten(0);
        return dat.laden(0, new Text("anyitem.png"));
    }
    else
    {
        return dynamic_cast<Framework::Bild*>(
            zItemType(itemType)->zIcon()->getThis());
    }
}

int RequirementSet::getItemType() const
{
    int itemType = -1;
    for (Requirement* req : requirements)
    {
        int rT = req->getItemType();
        if (rT != -1)
        {
            if (itemType == -1)
            {
                itemType = rT;
            }
            else if (itemType != rT)
            {
                itemType = -2;
            }
        }
    }
    return itemType;
}

LogicTree::LogicTree(LogicalOperator op, RequirementSet* set)
    : Framework::ReferenceCounter(),
      op(op),
      requirementSet(set)
{}

LogicTree::~LogicTree()
{
    if (requirementSet) requirementSet->release();
}

Framework::RCArray<RequirementSet>* LogicTree::multiply(
    Framework::RCArray<RequirementSet>* a,
    Framework::RCArray<RequirementSet>* b)
{
    Framework::RCArray<RequirementSet>* result
        = new Framework::RCArray<RequirementSet>();
    for (RequirementSet* aSet : *a)
    {
        for (RequirementSet* bSet : *b)
        {
            RequirementSet* set = aSet->clone();
            set->addRequirements(bSet);
            if (!set->isNoneMatch())
            {
                if (set->isAnyMatch())
                {
                    result->leeren();
                    result->add(set);
                    a->release();
                    b->release();
                    return result;
                }
                else
                {
                    result->add(set);
                }
            }
            else
            {
                set->release();
            }
        }
    }
    return result;
}

void LogicTree::addChildren(LogicTree* tree)
{
    children.add(tree);
}

LogicTree* LogicTree::clone()
{
    LogicTree* result = new LogicTree(op, requirementSet->clone());
    for (LogicTree* child : children)
    {
        result->children.add(child->clone());
    }
    return result;
}

void LogicTree::negate()
{
    if (requirementSet != 0)
    {
        requirementSet->negate(this);
        requirementSet->release();
        requirementSet = 0;
    }
    else
    {
        if (op == LogicalOperator::AND)
        {
            op = LogicalOperator::OR;
        }
        else if (op == LogicalOperator::OR)
        {
            op = LogicalOperator::AND;
        }
        for (LogicTree* child : children)
        {
            child->negate();
        }
    }
}

Framework::RCArray<RequirementSet>* LogicTree::resolve()
{
    if (requirementSet != 0)
    {
        Framework::RCArray<RequirementSet>* result
            = new Framework::RCArray<RequirementSet>();
        result->add(dynamic_cast<RequirementSet*>(requirementSet->getThis()));
        return result;
    }
    else
    {
        Framework::RCArray<RequirementSet>* result = 0;
        if (op == LogicalOperator::OR)
        {
            result = new Framework::RCArray<RequirementSet>();
            for (LogicTree* child : children)
            {
                Framework::RCArray<RequirementSet>* childSet = child->resolve();
                for (RequirementSet* set : *childSet)
                {
                    result->add(dynamic_cast<RequirementSet*>(set->getThis()));
                }
                childSet->release();
            }
        }
        else if (op == LogicalOperator::AND)
        {
            result = children.z(0)->resolve();
            for (int i = 1; i < children.getEintragAnzahl(); i++)
            {
                Framework::RCArray<RequirementSet>* childSet
                    = children.z(i)->resolve();
                result = multiply(result, childSet);
            }
        }
        if (result != 0)
        {
            for (int i = 0; i < result->getEintragAnzahl(); i++)
            {
                if (result->z(i)->isNoneMatch())
                {
                    result->remove(i);
                    i--;
                }
                else if (result->z(i)->isAnyMatch())
                {
                    Framework::RCArray<RequirementSet>* anyMatch
                        = new Framework::RCArray<RequirementSet>();
                    anyMatch->add(result->get(i));
                    result->release();
                    return anyMatch;
                }
            }
        }
        return result;
    }
}