/**
   * "Generate contents for the given BlockInfoVec on the given Link." [DTN2]
   *
   * @return "the total length of the formatted blocks for this bundle." [DTN2]
   */
  public static int generate_blocks(Bundle bundle, BlockInfoVec blocks, final Link link) {
    // "now assert there's at least 2 blocks (primary + payload) and
    // that the primary is first" [DTN2]
    assert (blocks.size() >= 2);
    assert (blocks.front().type() == bundle_block_type_t.PRIMARY_BLOCK);

    // "now we make a pass through the list and call generate on
    // each block processor" [DTN2]

    for (int i = 0; i < blocks.size(); i++) {
      boolean last = i == blocks.size() - 1;

      BlockInfo iter = blocks.get(i);

      iter.owner().generate(bundle, blocks, iter, link, last);

      BPF.getInstance()
          .getBPFLogger()
          .debug(
              TAG,
              String.format(
                  "generated block (owner %s type %s) "
                      + "data_offset %d data_length %d , contents_length %d",
                  iter.owner().block_type(),
                  iter.type(),
                  iter.data_offset(),
                  iter.data_length(),
                  iter.contents().position()));

      if (last) {
        assert ((iter.flags() & BundleProtocol.block_flag_t.BLOCK_FLAG_LAST_BLOCK.getCode()) != 0);
      } else {
        assert ((iter.flags() & BundleProtocol.block_flag_t.BLOCK_FLAG_LAST_BLOCK.getCode()) == 0);
      }
    }

    // "Now that all the EID references are added to the dictionary,
    // generate the primary block." [DTN2]
    PrimaryBlockProcessor pbp =
        (PrimaryBlockProcessor) find_processor(BundleProtocol.bundle_block_type_t.PRIMARY_BLOCK);
    assert (blocks.front().owner() == pbp);
    pbp.generate_primary(bundle, blocks, blocks.front());

    // "make a final pass through, calling finalize() and extracting
    // the block length" [DTN2]
    int total_len = 0;
    for (int i = blocks.size() - 1; i >= 0; i--) {
      BlockInfo iter = blocks.get(i);
      iter.owner().finalize(bundle, blocks, iter, link);
      total_len += iter.full_length();
    }

    return total_len;
  }
  /**
   * "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;
  }