/**
   * Delivery bundle process forward the bundle to BundleDaemon
   *
   * @param bundle Bundle to process.
   */
  @Override
  public void deliver_bundle(Bundle bundle) {

    int payload_len = bundle.payload().length();

    Logger.getInstance()
        .debug(TAG, String.format("%d byte ping from %s", payload_len, bundle.source().str()));

    Bundle reply = new Bundle(location_t.MEMORY);

    reply.source().assign(endpoint_);
    reply.dest().assign(bundle.source());
    reply.replyto().assign(EndpointID.NULL_EID());
    reply.custodian().assign(EndpointID.NULL_EID());
    reply.set_expiration(bundle.expiration());

    reply.payload().set_length(payload_len);
    reply.payload().write_data(bundle.payload(), 0, payload_len, 0);

    BundleDaemon.getInstance().post_at_head(new BundleDeliveredEvent(bundle, this));
    BundleDaemon.getInstance()
        .post_at_head(new BundleReceivedEvent(reply, event_source_t.EVENTSRC_ADMIN));
  }
  /** Main loop in Discovery */
  public void run() {

    Logger.getInstance().debug(TAG, "discovery thread running");
    IByteBuffer buf = new SerializableByteBuffer(1024);

    while (true) {
      if (shutdown_) break;

      /* Send section */
      try {
        int min_diff = INT_MAX;
        Iterator<Announce> i = list_.iterator();

        while (i.hasNext()) {
          IPAnnounce announce = (IPAnnounce) i.next();

          int remaining = announce.interval_remaining();

          if (remaining == 0) {
            try {
              // Logger.getInstance().debug(TAG,
              // "announce ready for sending");
              hdr = announce.format_advertisement(buf, 1024);

              buf.put(hdr.cl_type());
              buf.put(hdr.interval());
              buf.putShort(hdr.length());
              byte[] ip_address = new byte[] {0, 0, 0, 0};
              buf.put(ip_address);
              buf.putShort(hdr.inet_port());
              buf.putShort(hdr.name_len());
              byte[] name = hdr.sender_name().getBytes();
              buf.put(name);

              int l = hdr.length();
              data = new byte[l];
              int z;
              for (z = 0; z < l; z++) {
                data[z] = buf.get(z);
              }

              DatagramPacket pack =
                  new DatagramPacket(
                      data, data.length, InetAddress.getByName("255.255.255.255"), port_);
              socket_.send(pack);
              min_diff = announce.interval();
            } catch (Exception e) {
              Logger.getInstance().error(TAG, "error sending the packet " + e.getMessage());
            }
          } else {
            // Logger.getInstance().debug(TAG,
            // "Could not send discovery request");
            if (remaining < min_diff) {
              min_diff = announce.interval_remaining();
            }
          }
        }
      } catch (Exception e) {
        e.printStackTrace();
      }

      /* receive section */
      try {
        buf.rewind();

        byte[] Rdata = new byte[1024];

        DatagramPacket packet = new DatagramPacket(Rdata, Rdata.length);

        socket_.receive(packet);
        Logger.getInstance().debug("B4", "Received beacon: " + packet.getAddress());

        // String s = new String(packet.getData(), 0,
        // packet.getLength());
        // Logger.getInstance().debug(TAG, "Received response " + s);

        EndpointID remote_eid = new EndpointID();
        String nexthop = ""; // For now

        byte[] b = packet.getData();
        ByteBuffer bb = ByteBuffer.wrap(b);

        hdr = new DiscoveryHeader();
        hdr = new DiscoveryHeader();
        hdr.set_cl_type(bb.get());

        hdr.set_interval(bb.get());
        hdr.set_length(bb.getShort());

        byte[] addr = new byte[4];
        bb.get(addr);

        hdr.set_inet_addr(InetAddress.getByAddress(addr));
        hdr.set_inet_port(bb.getShort());
        short name_len = bb.getShort();
        hdr.set_name_len(name_len);
        byte[] name = new byte[name_len];
        bb.get(name);
        String sender_name = new String(name);
        hdr.set_sender_name(sender_name);
        remote_eid = new EndpointID(hdr.sender_name());
        // if(hdr.inet_addr().toString().equals("0.0.0.0"))
        nexthop = packet.getAddress().toString() + ":" + hdr.inet_port();
        // else
        // nexthop = hdr.inet_addr().toString()+":"+hdr.inet_port();

        String Type = IPDiscovery.type_to_str(cl_type_t.get(hdr.cl_type()));

        BundleDaemon BD = BundleDaemon.getInstance();

        if (remote_eid.equals(BD.local_eid())) {
          // Logger.getInstance().debug(TAG,
          // "ignoring beacon from self" + remote_eid);
        } else {
          // distribute to all beacons registered for this CL type
          handle_neighbor_discovered(Type, nexthop, remote_eid);
        }

        Logger.getInstance().debug("B4", "beacon: " + remote_eid);

      } catch (Exception e) {
        Logger.getInstance().info(TAG, "Fail receiving the UDP datagram " + e.getMessage());
      }
    }
  }
  /** "Constructor-like function to create a new custody signal bundle." [DTN2] */
  public static Bundle create_custody_signal(
      final Bundle orig_bundle,
      final EndpointID source_eid,
      boolean succeeded,
      BundleProtocol.custody_signal_reason_t reason) {

    Bundle bundle = new Bundle(location_t.MEMORY);

    bundle.source().assign(source_eid);
    if (orig_bundle.custodian().equals(EndpointID.NULL_EID())) {
      Logger.getInstance()
          .error(
              TAG,
              String.format(
                  "create_custody_signal(for bundle id %d): "
                      + "custody signal cannot be generated due to custodian is  null eid",
                  orig_bundle.bundleid()));
    }
    bundle.dest().assign(orig_bundle.custodian());
    bundle.replyto().assign(EndpointID.NULL_EID());
    bundle.custodian().assign(EndpointID.NULL_EID());
    bundle.set_is_admin(true);

    bundle.set_expiration(orig_bundle.expiration());

    int sdnv_encoding_len = 0;
    int signal_len = 0;

    // "format of custody signals:
    //
    // 1 byte admin payload type and flags
    // 1 byte status code
    // SDNV [Fragment Offset (if present)]
    // SDNV [Fragment Length (if present)]
    // SDNVx2 Time of custody signal
    // SDNVx2 Copy of bundle X's Creation Timestamp
    // SDNV Length of X's source endpoint ID
    // vari Source endpoint ID of bundle X

    //
    // first calculate the length
    //

    // the non-optional, fixed-length fields above:" [DTN2]
    signal_len = 1 + 1;

    // "the 2 SDNV fragment fields:" [DTN2]
    if (orig_bundle.is_fragment()) {
      signal_len += SDNV.encoding_len(orig_bundle.frag_offset());
      signal_len += SDNV.encoding_len(orig_bundle.orig_length());
    }

    // "Time field, set to the current time:" [DTN2]
    DTNTime now = new DTNTime();

    signal_len += DTNTime.SDNV_encoding_len(now);

    // "The bundle's creation timestamp:" [DTN2]
    signal_len += BundleTimestamp.SDNV_encoding_len(orig_bundle.creation_ts());

    // the Source Endpoint ID length and value
    signal_len += SDNV.encoding_len(orig_bundle.source().length()) + orig_bundle.source().length();

    //
    // "We got all the data ready, now format the buffer" [DTN2]
    //
    IByteBuffer bp = new SerializableByteBuffer(signal_len);
    int len = signal_len;
    bp.rewind();
    // "Admin Payload Type and flags" [DTN2]
    byte type_and_flags_byte =
        (byte) (BundleProtocol.admin_record_type_t.ADMIN_CUSTODY_SIGNAL.getCode() << 4);
    if (orig_bundle.is_fragment()) {
      type_and_flags_byte |= BundleProtocol.admin_record_flags_t.ADMIN_IS_FRAGMENT.getCode();
    }
    bp.put(type_and_flags_byte);
    len--;

    // Status_Flag_Byte consists of Success flag and reason code
    byte status_flag_byte = (byte) ((succeeded ? 1 : 0) << 7 | (reason.getCode() & 0x7f));
    bp.put(status_flag_byte);
    len--;

    // "The 2 Fragment Fields" [DTN2]
    if (orig_bundle.is_fragment()) {
      sdnv_encoding_len = SDNV.encode(orig_bundle.frag_offset(), bp, len);
      assert (sdnv_encoding_len > 0);
      len -= sdnv_encoding_len;

      sdnv_encoding_len = SDNV.encode(orig_bundle.orig_length(), bp, len);
      assert (sdnv_encoding_len > 0);
      len -= sdnv_encoding_len;
    }

    // DTNTime which is a time of signal field
    sdnv_encoding_len = DTNTime.SDNV_encoding_len(now);
    assert (sdnv_encoding_len > 0);
    DTNTime.encodeSDNV(now, bp);
    len -= sdnv_encoding_len;

    // "Copy of bundle X's Creation Timestamp" [DTN2]
    sdnv_encoding_len = BundleTimestamp.SDNV_encoding_len(orig_bundle.creation_ts());
    assert (sdnv_encoding_len > 0);
    BundleTimestamp.encodeSDNV(orig_bundle.creation_ts(), bp);
    len -= sdnv_encoding_len;

    // "The Endpoint ID length and data" [DTN2]
    sdnv_encoding_len = SDNV.encode(orig_bundle.source().length(), bp, len);
    assert (sdnv_encoding_len > 0);
    len -= sdnv_encoding_len;

    assert (len == orig_bundle.source().length());
    bp.put(orig_bundle.source().byte_array());

    //
    // "Finished generating the payload" [DTN2]
    //
    bp.rewind();
    bundle.payload().set_data(bp, signal_len);
    return bundle;
  }