bool RoutingCompiler::cmpRules(const RoutingRule &r1, const RoutingRule &r2)
{
    if (r1.getRDst()!=r2.getRDst()) return false;
    if (r1.getRGtw()!=r2.getRGtw()) return false;
    if (r1.getRItf()!=r2.getRItf()) return false;

    return true;
}
string RoutingCompiler::debugPrintRule(Rule *r)
{
    RoutingRule *rule = RoutingRule::cast(r);

    RuleElementRDst *dstrel = rule->getRDst();
    RuleElementRItf *itfrel = rule->getRItf();
    RuleElementRGtw *gtwrel = rule->getRGtw();

    ostringstream str;

//    str << setw(70) << setfill('-') << "-";

    string dst, itf, gtw;
   
    FWObject *obj = FWReference::getObject(itfrel->front());
    itf = (obj) ? obj->getName() : "NULL";

    obj = FWReference::getObject(gtwrel->front());
    gtw = (obj) ? obj->getName() : "NULL";
     
    
    int no = 0;
    FWObject::iterator i1 = dstrel->begin();
    while ( i1!=dstrel->end())
    {
        str << endl;

        dst = " ";

        if (i1 != dstrel->end())
        {
            FWObject *o = FWReference::getObject(*i1);
            dst = (o) ? o->getName() : "NULL";
        }

        int w = 0;
        if (no==0)
        {
            str << rule->getLabel();
            w = rule->getLabel().length();
        }
        
        str <<  setw(10-w)  << setfill(' ') << " ";

        str <<  setw(18) << setfill(' ') << dst.c_str() << " ";
        str <<  setw(18) << setfill(' ') << itf.c_str() << " ";
        str <<  setw(18) << setfill(' ') << gtw.c_str() << " ";
        str <<  setw(18) << setfill(' ') << " ";

        ++no;

        if ( i1 != dstrel->end() ) ++i1;
    }
    return str.str();
}
bool RoutingCompiler::interfaceOrGateway::processNext()
{
    RoutingRule *rule = getNext(); if (rule==NULL) return false;
    tmp_queue.push_back(rule);
    
    RuleElementRItf *itfrel = rule->getRItf();
    RuleElementRGtw *gtwrel = rule->getRGtw();

    if (!itfrel->isAny() && !gtwrel->isAny())
    {
        compiler->abort(rule,
                        "Use either gateway or interface in a routing rule "
                        "but not both at the same time");
    }

    return true;
}
/*
 * Call this after converting to atomic rules by DST to be sure there
 * is just one object in DST.
 */
bool RoutingCompiler::sameDestinationDifferentGateways::processNext()
{
    slurp();
    if (tmp_queue.size()==0) return false;

    // map destination to gateway.
    std::map<string, string> dst_to_gw;
    std::map<string, string> dst_to_rule;

    for (deque<Rule*>::iterator k=tmp_queue.begin(); k!=tmp_queue.end(); ++k)
    {
        RoutingRule *rule = RoutingRule::cast( *k );
        
        RuleElementRDst *dstrel = rule->getRDst();
        Address *dst = Address::cast(FWReference::getObject(dstrel->front()));
        const InetAddr* dst_addr = dst->getAddressPtr();
        const InetAddr* dst_netm = dst->getNetmaskPtr();
        string key = dst_addr->toString() + "/" + dst_netm->toString();

        // RuleElementRItf *itfrel = rule->getRItf();
        // FWObject *itf = FWReference::cast(itfrel->front())->getPointer();
    
        RuleElementRGtw *gtwrel = rule->getRGtw();
        Address *gtw = Address::cast(FWReference::getObject(gtwrel->front()));
        const InetAddr* gtw_addr = gtw->getAddressPtr();
        const InetAddr* gtw_netm = gtw->getNetmaskPtr();
        string val = gtw_addr->toString() + "/" + gtw_netm->toString();

        if (!dst_to_gw[key].empty() && dst_to_gw[key] != val)
        {
            compiler->abort(
                rule,
                "Rules " + dst_to_rule[key] + " and " + rule->getLabel() +
                " define routes to the same destination " + key +
                " via different gateways. This configuration is not supported"
                " for " + compiler->fw->getStr("host_OS"));
        } else
        {
            dst_to_gw[key] = val;
            dst_to_rule[key] = rule->getLabel();
        }
    }

    return true;
}
bool RoutingCompiler::classifyRoutingRules::processNext()
{

    assert(compiler!=NULL);
    assert(prev_processor!=NULL);

    slurp();

    if (tmp_queue.size()==0) return false;
    
    
    for (std::deque<libfwbuilder::Rule *>::iterator tmp_queue_it=tmp_queue.begin(); tmp_queue_it!=tmp_queue.end(); ++tmp_queue_it)
    {
        RoutingRule *rule = RoutingRule::cast( *tmp_queue_it);
        rule->setRuleType( RoutingRule::SinglePath);
        
        RuleElementRItf *itfrel=rule->getRItf();
        FWObject *itf = FWReference::cast(itfrel->front())->getPointer();
        
        RuleElementRGtw *gtwrel=rule->getRGtw();
        FWObject *gtw = FWReference::cast(gtwrel->front())->getPointer();
        
        string metric  = rule->getMetricAsString();
        string label   = rule->getSortedDstIds();
        ostringstream ostr;
        ostr << gtw->getId() << "_" << itf->getId();
        string combiId = ostr.str();
        
        if( label == "")
            compiler->abort(
                
                    rule,
                    "Place 'createSortedDstIdsLabel()' right before "
                    "'classifyRoutingRules()' in the rule processor chain");
        
        dest_it = rules_seen_so_far.find(label);
        if( dest_it != rules_seen_so_far.end()) {
            
            // a rule with the same destination was already seen
            //std::cout << "classifyRoutingRules:NO NEW DEST" << std::endl;///
            
            gtwitf_it = dest_it->second.find(combiId);
            if( gtwitf_it == dest_it->second.end() ) {
                
                // ... this gateway and interface combination is new for this destination
                //std::cout << "classifyRoutingRules:NEW GTWITF" << std::endl;///
                
                
                for( gtwitf_it = dest_it->second.begin(); gtwitf_it != dest_it->second.end(); gtwitf_it++) {
                    
                    if( gtwitf_it->second.first == metric) {
                        // ... but has the same metric as another rule with this Dst => multipath or abort
                        //std::cout << "classifyRoutingRules:SAME METRIC" << std::endl;///
                        
                        if(true) { //TODO: if ( compiler->fw->getOptionsObject()->getBool ("equal_cost_multi_path") )
                            
                            rule->setRuleType( RoutingRule::MultiPath);
                            gtwitf_it->second.second->setRuleType( RoutingRule::MultiPath);
                            
                            //std::cout << "classifyRoutingRules:the rules " << rule->getLabel() << " and " << gtwitf_it->second.second->getLabel() << " were set to multipath." << std::endl;///
                        }
                    }
                }
                
                dest_it->second[combiId] = pair< string, RoutingRule*>( metric, rule);
            }
            
        } else {
            
            // this destination is new
            //std::cout << "classifyRoutingRules:NEW DEST" << std::endl;///
             
            map< string, pair< string, RoutingRule*> > gtw_itf_tmp;
            gtw_itf_tmp[combiId] = pair< string, RoutingRule*>( metric, rule);
            rules_seen_so_far[label] = gtw_itf_tmp;
        }
    }
    
    return true;
}
bool RoutingCompiler::competingRules::processNext()
{
    RoutingRule *rule = getNext(); if (rule==NULL) return false;
        
    RuleElementRItf *itfrel = rule->getRItf();
    FWObject *itf = FWReference::cast(itfrel->front())->getPointer();
    
    RuleElementRGtw *gtwrel = rule->getRGtw();
    FWObject *gtw = FWReference::cast(gtwrel->front())->getPointer();
     
    string metric = rule->getMetricAsString();
    string label  = rule->getSortedDstIds();
    ostringstream ostr;
    ostr << gtw->getId() << "_" << itf->getId();
    string combiId = ostr.str();
    
    if( label == "") compiler->abort(
        
            rule,         
            "Place 'createSortedDstIdsLabel()' before 'competingRules()' "
            "in the rule processor chain");
    
    dest_it = rules_seen_so_far.find(label);
    if( dest_it != rules_seen_so_far.end()) {
        
        // a rule with the same destination was already seen
        ///std::cout << "NO NEW DEST" << std::endl;
        
        gtwitf_it = dest_it->second.find(combiId);
        if( gtwitf_it != dest_it->second.end() )
        {
            // ... this gateway and interface combination were already
            // seen for this destination
            ///std::cout << "NO NEW GTWITF" << std::endl;
            
            if( gtwitf_it->second.first == metric) {
                
                // ... and same metric => rule already exists, skip
                ///std::cout << "SAME METRIC" << std::endl;
                
                string msg;
                msg = "Routing rules " + gtwitf_it->second.second +
                    " and " + rule->getLabel() +
                    " are identical, skipping the second one. " +
                    "Delete one of them to avoid this warning";
                compiler->warning(rule,  msg.c_str());
            } else {
                
                // ... but different metric => what metric should I use? => abort
                ///std::cout << "DIFFERENT METRIC" << std::endl;
                
                string msg;
                msg = "Routing rules " + gtwitf_it->second.second +
                    " and " + rule->getLabel() +
                    " are identical except for the metric, " +
                    "please delete one of them";
                compiler->abort(rule,  msg.c_str());
            }
        
        } else
        {
            // ... this gateway and interface combination is new for
            // this destination
            ///std::cout << "NEW GTWITF" << std::endl;///
            
            if(false)
            {
                // TODO_lowPrio: if (
                // !compiler->fw->getOptionsObject()->getBool
                // ("equal_cost_multi_path") ) ...If multipath is
                // turned off, perform this check.

                // iterate all gtwitf combis in the map
                // dest_it->second and search for the current metric
                
                // ... but has the same metric => what route should I
                // use for this destination? => abort
                    
                string msg;
                msg = "Routing rules " + gtwitf_it->second.second + " and " +
                    rule->getLabel() +
                    " have the same destination and same metric,"
                    "but different gateway and interface combination. "
                    "Set the metrics to different values or "
                    "enable ECMP (Equal Cost MultiPath) routing";
                compiler->abort( msg.c_str() );
            
            } else
            {
                // ... and different metric OR equal_cost_multi_path enabled => OK
                tmp_queue.push_back(rule);
            }
            
            dest_it->second[combiId] =
                pair< string, string>( metric, rule->getLabel());
        }
        
    } else {
        
        // this destination is new
        //std::cout << "NEW DEST" << std::endl;
        ///
        
        //ruleinfo tmpRuleInfo = { gtw->getStr("id") + itf->getStr("id"), metric, rule->getLabel()};
        //rules_seen_so_far[label] = tmpRuleInfo;
        
        map< string, pair< string, string> > gtw_itf_tmp;
        gtw_itf_tmp[combiId] = pair< string, string>( metric, rule->getLabel());
        
        rules_seen_so_far[label] = gtw_itf_tmp;
        
        
        tmp_queue.push_back(rule);
    }

    return true;
}