/**
   * Converse of addRule. Since we know all the rules ever stored in this flowmap we simply retrieve
   * the rule using the id provided and remove each of its fields from the underlying structures.
   *
   * @param id - the id of the rule to remove
   * @throws FlowEntryNotFound - if this id is unknown. Could indicate that rule has already been
   *     removed.
   * @return
   */
  public void removeRule(int id) throws FlowEntryNotFound {
    ruleCount--;

    if (!rules.containsKey(id)) throw new FlowEntryNotFound(id);

    FlowEntry rule = rules.get(id);

    BitSet flowRuleSet = getFlowRuleSet(rule);

    remove(port, rule.getRuleMatch().getInputPort(), flowRuleSet);
    remove(dpids, rule.dpid, flowRuleSet);
    remove(vlan, (int) rule.getRuleMatch().getDataLayerVirtualLan(), flowRuleSet);
    remove(vlan, rule.getRuleMatch().getDataLayerVirtualLanPriorityCodePoint() << 16, flowRuleSet);
    remove(dl_type, rule.getRuleMatch().getDataLayerType(), flowRuleSet);

    remove(nw, (short) rule.getRuleMatch().getNetworkProtocol(), flowRuleSet);
    remove(nw, (short) (rule.getRuleMatch().getNetworkTypeOfService() << 8), flowRuleSet);

    remove(tp, (int) rule.getRuleMatch().getTransportSource(), flowRuleSet);
    remove(tp, rule.getRuleMatch().getTransportDestination() << 16, flowRuleSet);

    remove(dl_src, FVMatch.toLong(rule.getRuleMatch().getDataLayerSource()), flowRuleSet);
    remove(dl_dst, FVMatch.toLong(rule.getRuleMatch().getDataLayerDestination()), flowRuleSet);

    remove(prioSet, rule.getPriority(), flowRuleSet);

    vlan_ignore.clear(rule.getId());
    rules.remove(rule.getId());
    allRules.andNot(flowRuleSet);
  }
  /**
   * match is called when a switch sends a packet in to the controller. The goal is to find all flow
   * space rules which match the fields of the packet in.
   *
   * @param dpid - the datapath id of the switch which sent the packet in.
   * @param match - the packet in information
   * @return a list, sorted by priority, of flow space rules which match.
   */
  public List<FlowEntry> match(long dpid, FVMatch match) {
    BitSet set = new BitSet();
    LinkedList<FlowEntry> flowrules = new LinkedList<FlowEntry>();
    int wildcards = match.getWildcards();
    FVLog.log(LogLevel.DEBUG, null, "dpid: ", dpid, "match: ", match.toString());

    set.or(allRules);
    FVLog.log(LogLevel.DEBUG, null, "allRules: ", set.toString());

    try {

      testEmpty(set, dpids, dpid, FlowEntry.ALL_DPIDS, wildcards, 0);
      /*
       * Test every field and intersect the resulting bitset. If the bit
       * set is empty an exception is thrown to stop the search.
       */
      testEmpty(set, port, match.getInputPort(), ANY_IN_PORT, wildcards, FVMatch.OFPFW_IN_PORT);
      if (match.getDataLayerVirtualLan() != ANY_VLAN_ID) {
        testEmpty(
            set,
            vlan,
            (int) match.getDataLayerVirtualLan(),
            (int) ANY_VLAN_ID,
            wildcards,
            FVMatch.OFPFW_DL_VLAN);
        testEmpty(
            set,
            vlan,
            match.getDataLayerVirtualLanPriorityCodePoint() << 16,
            ANY_VLAN_PCP << 16,
            wildcards,
            FVMatch.OFPFW_DL_VLAN_PCP);
      } else {

        set.andNot(vlan_ignore);
      }

      testEmpty(
          set, dl_type, match.getDataLayerType(), ANY_ETHER, wildcards, FVMatch.OFPFW_DL_TYPE);

      testEmpty(
          set,
          nw,
          (short) match.getNetworkProtocol(),
          (short) ANY_NW_PROTO_TOS,
          wildcards,
          FVMatch.OFPFW_NW_PROTO);

      testEmpty(
          set,
          nw,
          (short) (match.getNetworkTypeOfService() << 8),
          (short) (ANY_NW_PROTO_TOS << 8),
          wildcards,
          FVMatch.OFPFW_NW_TOS);

      testEmpty(
          set, tp, (int) match.getTransportSource(), (int) ANY_TP, wildcards, FVMatch.OFPFW_TP_SRC);

      testEmpty(
          set,
          tp,
          match.getTransportDestination() << 16,
          ANY_TP << 16,
          wildcards,
          FVMatch.OFPFW_TP_DST);

      testEmpty(
          set,
          dl_src,
          FVMatch.toLong(match.getDataLayerSource()),
          ANY_MAC,
          wildcards,
          FVMatch.OFPFW_DL_SRC);

      testEmpty(
          set,
          dl_dst,
          FVMatch.toLong(match.getDataLayerDestination()),
          ANY_MAC,
          wildcards,
          FVMatch.OFPFW_DL_DST);

      for (int i = set.nextSetBit(0); i >= 0; i = set.nextSetBit(i + 1)) {
        FlowEntry fe = rules.get(i);
        FVMatch ruleMatch = fe.getRuleMatch();
        FlowIntersect inter;
        inter = new FlowIntersect(fe.clone());

        FVMatch interMatch = inter.getMatch();
        interMatch.setNetworkDestination(
            testIP(
                inter,
                FVMatch.OFPFW_NW_DST_SHIFT,
                match.getNetworkDestinationMaskLen(),
                ruleMatch.getNetworkDestinationMaskLen(),
                match.getNetworkDestination(),
                ruleMatch.getNetworkDestination()));
        if (inter.getMatchType() == MatchType.NONE) {
          set.clear(i);
          continue;
        }

        // test ip_src
        interMatch.setNetworkSource(
            testIP(
                inter,
                FVMatch.OFPFW_NW_SRC_SHIFT,
                match.getNetworkSourceMaskLen(),
                ruleMatch.getNetworkSourceMaskLen(),
                match.getNetworkSource(),
                ruleMatch.getNetworkSource()));
        if (inter.getMatchType() == MatchType.NONE) {
          set.clear(i);
          continue;
        }
      }

    } catch (NoMatch e) {
      FVLog.log(LogLevel.INFO, null, "No match for: ", match);
      return flowrules;
    }

    /*
     * If we got here we have a match. Now return a prioritized list of
     * matches.
     *
     * Higher numbers have higher priorities.
     */

    TreeSet<FlowEntry> entries = new TreeSet<FlowEntry>();
    for (int i = set.nextSetBit(0); i >= 0; i = set.nextSetBit(i + 1)) {
      entries.add(rules.get(i));
    }
    flowrules.addAll(entries);
    return flowrules;
  }
  /**
   * This is called when a controller issues a FlowMod. The goal here is to rewrite flowmods so that
   * they guarantee traffic isolation between slices. A flowmod is rewritten following these rules:
   *
   * <p>1. Its field is wildcarded, then for every potentially matching rule we rewrite the field
   * with the value in the rule.
   *
   * <p>2. Its field is specified, then leave it untouched, but use it for matching purposes.
   *
   * <p>3. We have a potential partial match: 3.1. The field and the flowrule field do not overlap,
   * return no match. 3.2. The fields overlap, return the smallest possible intersection of the two.
   *
   * <p>Currently rule 3 only applies to IP addresses, but this could change in the future.
   *
   * @param dpid - The dpid this flowmod is directed to, could be wildcarded.
   * @param match - The set of field modifications issued by this flowmod.
   * @return a list of intersections (ie. flowmod rewrites) created by the flowmod issued from the
   *     controller.
   */
  public List<FlowIntersect> intersect(long dpid, FVMatch match) {
    FVLog.log(LogLevel.DEBUG, null, "dpid: ", dpid, " match: ", match.toString());
    BitSet set = new BitSet();
    normalize(match);
    int wildcards = match.getWildcards();
    TreeSet<FlowIntersect> ret = new TreeSet<FlowIntersect>();
    HashMap<Integer, FlowIntersect> intersections = new HashMap<Integer, FlowIntersect>();
    HashMap<Integer, Pair<Boolean, BitSet>> rewrites =
        new HashMap<Integer, Pair<Boolean, BitSet>>();

    set.or(allRules);

    try {

      testEmpty(set, dpids, dpid, FlowEntry.ALL_DPIDS, wildcards, 0);

      rewrites.put(
          FVMatch.OFPFW_IN_PORT,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set, port, match.getInputPort(), ANY_IN_PORT, wildcards, FVMatch.OFPFW_IN_PORT),
              set));

      rewrites.put(
          FVMatch.OFPFW_DL_SRC,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  dl_src,
                  FVMatch.toLong(match.getDataLayerSource()),
                  ANY_MAC,
                  wildcards,
                  FVMatch.OFPFW_DL_SRC),
              set));

      rewrites.put(
          FVMatch.OFPFW_DL_DST,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  dl_dst,
                  FVMatch.toLong(match.getDataLayerDestination()),
                  ANY_MAC,
                  wildcards,
                  FVMatch.OFPFW_DL_DST),
              set));

      rewrites.put(
          FVMatch.OFPFW_DL_VLAN,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  vlan,
                  (int) match.getDataLayerVirtualLan(),
                  (int) ANY_VLAN_ID,
                  wildcards,
                  FVMatch.OFPFW_DL_VLAN),
              set));

      rewrites.put(
          FVMatch.OFPFW_DL_VLAN_PCP,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  vlan,
                  match.getDataLayerVirtualLanPriorityCodePoint() << 16,
                  ANY_VLAN_PCP << 16,
                  wildcards,
                  FVMatch.OFPFW_DL_VLAN_PCP),
              set));
      rewrites.put(
          FVMatch.OFPFW_DL_TYPE,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  dl_type,
                  match.getDataLayerType(),
                  ANY_ETHER,
                  wildcards,
                  FVMatch.OFPFW_DL_TYPE),
              set));

      rewrites.put(
          FVMatch.OFPFW_NW_PROTO,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  nw,
                  (short) match.getNetworkProtocol(),
                  (short) ANY_NW_PROTO_TOS,
                  wildcards,
                  FVMatch.OFPFW_NW_PROTO),
              set));

      rewrites.put(
          FVMatch.OFPFW_NW_TOS,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  nw,
                  (short) (match.getNetworkTypeOfService() << 8),
                  (short) (ANY_NW_PROTO_TOS << 8),
                  wildcards,
                  FVMatch.OFPFW_NW_TOS),
              set));

      rewrites.put(
          FVMatch.OFPFW_TP_SRC,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  tp,
                  (int) match.getTransportSource(),
                  (int) ANY_TP,
                  wildcards,
                  FVMatch.OFPFW_TP_SRC),
              set));

      rewrites.put(
          FVMatch.OFPFW_TP_DST,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  tp,
                  match.getTransportDestination() << 16,
                  ANY_TP << 16,
                  wildcards,
                  FVMatch.OFPFW_TP_DST),
              set));

      int field = 0;
      boolean rewrite = false;
      BitSet inters = null;
      FlowIntersect flow = null;
      FlowEntry rule = null;

      for (Entry<Integer, Pair<Boolean, BitSet>> entry : rewrites.entrySet()) {
        field = entry.getKey();
        rewrite = entry.getValue().getFirst();
        inters = entry.getValue().getSecond();
        FVLog.log(LogLevel.DEBUG, null, "Rule ids which intersect: ", inters.toString());
        for (int i = inters.nextSetBit(0); i >= 0; i = inters.nextSetBit(i + 1)) {
          rule = rules.get(i).clone();
          flow = getIntersect(rule, intersections);

          if (!rewrite) {
            rule.setRuleMatch(match);
            setField(flow, rule.getRuleMatch(), field);
          }

          FVMatch ruleMatch = rules.get(i).getRuleMatch();
          FlowIntersect inter = flow;
          FVMatch interMatch = inter.getMatch();
          interMatch.setNetworkDestination(
              testIP(
                  inter,
                  FVMatch.OFPFW_NW_DST_SHIFT,
                  match.getNetworkDestinationMaskLen(),
                  ruleMatch.getNetworkDestinationMaskLen(),
                  match.getNetworkDestination(),
                  ruleMatch.getNetworkDestination()));
          if (inter.getMatchType() == MatchType.NONE) continue;

          interMatch.setNetworkSource(
              testIP(
                  inter,
                  FVMatch.OFPFW_NW_SRC_SHIFT,
                  match.getNetworkSourceMaskLen(),
                  ruleMatch.getNetworkSourceMaskLen(),
                  match.getNetworkSource(),
                  ruleMatch.getNetworkSource()));
          if (inter.getMatchType() == MatchType.NONE) continue;
          intersections.put(flow.getFlowEntry().getId(), flow);
        }
      }

      /*
       * Need to resolve intersection by priority.
       *
       * In the worst case this will be O(n) where n is the number of
       * rules. But in average we will only have a small subset of the
       * rules. Right??
       */
      ret.addAll(intersections.values());
      FVLog.log(LogLevel.DEBUG, null, "Intersections: ", intersections);

    } catch (NoMatch e) {
      FVLog.log(LogLevel.FATAL, null, "Failed to intersect flow mod " + match);
      return new ArrayList<FlowIntersect>(ret);
    } catch (UnknownMatchField umf) {
      FVLog.log(LogLevel.FATAL, null, umf.getMessage());
    }

    return new ArrayList<FlowIntersect>(ret);
  }
  /**
   * Adds a rule to the flowmap. It does so by exploding the rule into its fields and storing each
   * field into its independent structure.
   *
   * @param rule - the rule which will be added to the flowmap
   * @return
   */
  public void addRule(FlowEntry rule) {
    ruleCount++;
    allRules.set(rule.getId());
    BitSet flowRuleSet = getFlowRuleSet(rule);

    add(dpids, rule.dpid, flowRuleSet);

    add(
        port,
        (rule.getRuleMatch().getWildcards() & FVMatch.OFPFW_IN_PORT) != 0
            ? ANY_IN_PORT
            : rule.getRuleMatch().getInputPort(),
        flowRuleSet);

    short vid = rule.getRuleMatch().getDataLayerVirtualLan();
    int vpcp = rule.getRuleMatch().getDataLayerVirtualLanPriorityCodePoint();

    /*
     * Openflowj sets unspecified fields to zero.
     */
    if (vid != 0 || vpcp != 0) {
      vlan_ignore.set(rule.getId());
    }

    add(
        vlan,
        (rule.getRuleMatch().getWildcards() & FVMatch.OFPFW_DL_VLAN) != 0
            ? (int) ANY_VLAN_ID
            : (int) rule.getRuleMatch().getDataLayerVirtualLan(),
        flowRuleSet);

    add(
        vlan,
        (rule.getRuleMatch().getWildcards() & FVMatch.OFPFW_DL_VLAN_PCP) != 0
            ? ANY_VLAN_PCP << 16
            : rule.getRuleMatch().getDataLayerVirtualLanPriorityCodePoint() << 16,
        flowRuleSet);

    add(
        dl_type,
        (rule.getRuleMatch().getWildcards() & FVMatch.OFPFW_DL_TYPE) != 0
            ? ANY_ETHER
            : rule.getRuleMatch().getDataLayerType(),
        flowRuleSet);

    add(
        nw,
        (rule.getRuleMatch().getWildcards() & FVMatch.OFPFW_NW_PROTO) != 0
            ? ANY_NW_PROTO_TOS
            : (short) rule.getRuleMatch().getNetworkProtocol(),
        flowRuleSet);

    add(
        nw,
        (rule.getRuleMatch().getWildcards() & FVMatch.OFPFW_NW_TOS) != 0
            ? (short) (ANY_NW_PROTO_TOS << 8)
            : (short) (rule.getRuleMatch().getNetworkTypeOfService() << 8),
        flowRuleSet);

    add(
        tp,
        (rule.getRuleMatch().getWildcards() & FVMatch.OFPFW_TP_SRC) != 0
            ? ANY_TP
            : (int) rule.getRuleMatch().getTransportSource(),
        flowRuleSet);

    add(
        tp,
        (rule.getRuleMatch().getWildcards() & FVMatch.OFPFW_TP_DST) != 0
            ? ANY_TP << 16
            : rule.getRuleMatch().getTransportDestination() << 16,
        flowRuleSet);

    add(
        dl_src,
        (rule.getRuleMatch().getWildcards() & FVMatch.OFPFW_DL_SRC) != 0
            ? ANY_MAC
            : FVMatch.toLong(rule.getRuleMatch().getDataLayerSource()),
        flowRuleSet);
    add(
        dl_dst,
        (rule.getRuleMatch().getWildcards() & FVMatch.OFPFW_DL_DST) != 0
            ? ANY_MAC
            : FVMatch.toLong(rule.getRuleMatch().getDataLayerDestination()),
        flowRuleSet);

    add(prioSet, rule.getPriority(), flowRuleSet);
    FVLog.log(LogLevel.DEBUG, null, "prioSet:", prioSet);
    rules.put(rule.getId(), rule);
    getPrioSetRange();
  }