/**
   * "Generate a BlockInfoVec for the outgoing link and put it into xmit_blocks_." [DTN2]
   *
   * @return a list of new BLockInfo
   */
  public static BlockInfoVec prepare_blocks(Bundle bundle, final Link link) {
    // "create a new block list for the outgoing link by first calling
    // prepare on all the BlockProcessor classes for the blocks that
    // arrived on the link" [DTN2]
    BlockInfoVec xmit_blocks = bundle.xmit_link_block_set().create_blocks(link);
    BlockInfoVec recv_blocks = bundle.recv_blocks();

    if (recv_blocks.size() > 0) {
      // "if there is a received block, the first one better be the primary"
      // [DTN2]
      assert (recv_blocks.front().type() == bundle_block_type_t.PRIMARY_BLOCK);

      Iterator<BlockInfo> iter = recv_blocks.iterator();
      while (iter.hasNext()) {
        BlockInfo block = iter.next();

        if (bundle.fragmented_incoming()
            && xmit_blocks.find_block(BundleProtocol.bundle_block_type_t.PAYLOAD_BLOCK) != null) {
          continue;
        }

        block
            .owner()
            .prepare(bundle, xmit_blocks, block, link, BlockInfo.list_owner_t.LIST_RECEIVED);
      }

    } else {
      BPF.getInstance().getBPFLogger().debug(TAG, "adding primary and payload block");
      BlockProcessor bp = find_processor(BundleProtocol.bundle_block_type_t.PRIMARY_BLOCK);
      bp.prepare(bundle, xmit_blocks, null, link, BlockInfo.list_owner_t.LIST_NONE);
      bp = find_processor(bundle_block_type_t.PAYLOAD_BLOCK);
      bp.prepare(bundle, xmit_blocks, null, link, BlockInfo.list_owner_t.LIST_NONE);
    }

    // "now we also make sure to prepare() on any registered processors
    // that don't already have a block in the output list. this
    // handles the case where we have a locally generated block with
    // nothing in the recv_blocks vector" [DTN2]

    Iterator<BlockProcessor> itr = processors_.iterator();
    while (itr.hasNext()) {
      BlockProcessor bp = itr.next();
      if (!xmit_blocks.has_block(bp.block_type())) {
        bp.prepare(bundle, xmit_blocks, null, link, BlockInfo.list_owner_t.LIST_NONE);
      }
    }

    // Now prepare security blocks
    BPF.getInstance().getBPFLogger().debug(TAG, "Now preparing security blocks...");
    Security.prepare_out_blocks(bundle, link, xmit_blocks);

    return xmit_blocks;
  }
  /**
   * "Loop through the bundle's received block list to validate each entry." [DTN2]
   *
   * @return "true if the bundle is valid, false if it should be deleted." [DTN2]
   */
  public static boolean validate(
      Bundle bundle,
      status_report_reason_t[] reception_reason,
      status_report_reason_t[] deletion_reason) {
    int primary_blocks = 0, payload_blocks = 0;
    BlockInfoVec recv_blocks = bundle.recv_blocks();

    // "a bundle must include at least two blocks (primary and payload)"
    // [DTN2]
    if (recv_blocks.size() < 2) {
      BPF.getInstance().getBPFLogger().error(TAG, "bundle fails to contain at least two blocks");
      deletion_reason[0] = BundleProtocol.status_report_reason_t.REASON_BLOCK_UNINTELLIGIBLE;
      return false;
    }

    // "the first block of a bundle must be a primary block" [DTN2]
    if (recv_blocks.front().type() != BundleProtocol.bundle_block_type_t.PRIMARY_BLOCK) {
      BPF.getInstance().getBPFLogger().error(TAG, "bundle fails to contain a primary block");
      deletion_reason[0] = BundleProtocol.status_report_reason_t.REASON_BLOCK_UNINTELLIGIBLE;
      return false;
    }

    // "validate each individual block" [DTN2]

    int last_block_index = recv_blocks.size() - 1;
    for (int i = 0; i < recv_blocks.size(); i++) {
      BlockInfo current_block = recv_blocks.get(i);

      // "a block may not have enough data for the preamble" [DTN2]
      if (current_block.data_offset() == 0) {
        // "either the block is not the last one and something went
        // badly wrong, or it is the last block present" [DTN2]
        if (i != last_block_index) {
          BPF.getInstance().getBPFLogger().error(TAG, "bundle block too short for the preamble");
          deletion_reason[0] = BundleProtocol.status_report_reason_t.REASON_BLOCK_UNINTELLIGIBLE;
          return false;
        }
        // "this is the last block, so drop it" [DTN2]
        BPF.getInstance().getBPFLogger().debug(TAG, "forgetting preamble-starved last block");
        recv_blocks.remove(current_block);
        if (recv_blocks.size() < 2) {
          BPF.getInstance()
              .getBPFLogger()
              .error(TAG, "bundle fails to contain at least two blocks");
          deletion_reason[0] = BundleProtocol.status_report_reason_t.REASON_BLOCK_UNINTELLIGIBLE;
          return false;
        }
        // "continue with the tests; results may have changed now that
        // a different block is last" [DTN2]
        return false;
      } else {
        if (current_block.type() == BundleProtocol.bundle_block_type_t.PRIMARY_BLOCK) {
          primary_blocks++;
        }

        if (current_block.type() == BundleProtocol.bundle_block_type_t.PAYLOAD_BLOCK) {
          payload_blocks++;
        }
      }

      if (!current_block
          .owner()
          .validate(bundle, recv_blocks, current_block, reception_reason, deletion_reason)) {
        return false;
      }

      // "a bundle's last block must be flagged as such,
      // and all other blocks should not be flagged" [DTN2]
      if (i == last_block_index) {
        if (!current_block.last_block()) {
          if (!bundle.fragmented_incoming()) {
            BPF.getInstance().getBPFLogger().error(TAG, "bundle's last block not flagged");
            deletion_reason[0] = BundleProtocol.status_report_reason_t.REASON_BLOCK_UNINTELLIGIBLE;
            return false;
          } else {
            BPF.getInstance()
                .getBPFLogger()
                .debug(TAG, "bundle's last block not flagged, but " + "it is a reactive fragment");
          }
        }
      } else {
        if (current_block.last_block()) {
          BPF.getInstance().getBPFLogger().error(TAG, "bundle block incorrectly flagged as last");
          deletion_reason[0] = BundleProtocol.status_report_reason_t.REASON_BLOCK_UNINTELLIGIBLE;
          return false;
        }
      }
    }

    // "a bundle must contain one, and only one, primary block" [DTN2]
    if (primary_blocks != 1) {
      BPF.getInstance()
          .getBPFLogger()
          .error(TAG, String.format("bundle contains %d primary blocks", primary_blocks));
      deletion_reason[0] = BundleProtocol.status_report_reason_t.REASON_BLOCK_UNINTELLIGIBLE;
      return false;
    }

    // "a bundle must not contain more than one payload block" [DTN2]
    if (payload_blocks > 1) {
      BPF.getInstance()
          .getBPFLogger()
          .error(TAG, String.format("bundle contains %d payload blocks", payload_blocks));
      deletion_reason[0] = BundleProtocol.status_report_reason_t.REASON_BLOCK_UNINTELLIGIBLE;
      return false;
    }

    return true;
  }