/**
 * Transmit our HELLO message to the given (connected) neighbour.
 *
 * @param cls the 'HELLO' message
 * @param peer identity of the peer
 * @param address the address
 * @param state current state this peer is in
 * @param state_timeout timeout for the current state of the peer
 * @param bandwidth_in inbound quota in NBO
 * @param bandwidth_out outbound quota in NBO
 */
static void
transmit_our_hello (void *cls,
                    const struct GNUNET_PeerIdentity *peer,
		    const struct GNUNET_HELLO_Address *address,
		    enum GNUNET_TRANSPORT_PeerState state,
		    struct GNUNET_TIME_Absolute state_timeout,
		    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
		    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
{
  const struct GNUNET_MessageHeader *hello = cls;

  if (0 ==
      memcmp (peer,
              &GST_my_identity,
              sizeof (struct GNUNET_PeerIdentity)))
    return; /* not to ourselves */
  if (GNUNET_NO == GST_neighbours_test_connected (peer))
    return;

  GST_neighbours_send (peer,
		       hello,
		       ntohs (hello->size),
		       hello_expiration,
                       NULL, NULL);
}
/**
 * Function called for each of our connected neighbours.  Notify the
 * client about the existing neighbour.
 *
 * @param cls the `struct TransportClient *` to notify
 * @param peer identity of the neighbour
 * @param address the address
 * @param state the current state of the peer
 * @param state_timeout the time out for the state
 * @param bandwidth_in inbound bandwidth in NBO
 * @param bandwidth_out outbound bandwidth in NBO
 */
static void
notify_client_about_neighbour (void *cls,
    const struct GNUNET_PeerIdentity *peer,
    const struct GNUNET_HELLO_Address *address,
    enum GNUNET_TRANSPORT_PeerState state,
    struct GNUNET_TIME_Absolute state_timeout,
    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
{
  struct TransportClient *tc = cls;
  struct ConnectInfoMessage *cim;
  size_t size = sizeof (struct ConnectInfoMessage);
  char buf[size] GNUNET_ALIGN;

  if (GNUNET_NO == GST_neighbours_test_connected (peer))
    return;

  GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
  cim = (struct ConnectInfoMessage *) buf;
  cim->header.size = htons (size);
  cim->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
  cim->id = *peer;
  cim->quota_in = bandwidth_in;
  cim->quota_out = bandwidth_out;
  unicast (tc, &cim->header, GNUNET_NO);
}
/**
 * We received some payload.  Prepare to pass it on to our clients.
 *
 * @param peer (claimed) identity of the other peer
 * @param address the address
 * @param session session used
 * @param message the message to process
 * @param ats performance information
 * @param ats_count number of records in ats
 * @return how long the plugin should wait until receiving more data
 */
static struct GNUNET_TIME_Relative
process_payload (const struct GNUNET_PeerIdentity *peer,
                 const struct GNUNET_HELLO_Address *address,
                 struct Session *session,
                 const struct GNUNET_MessageHeader *message,
                 const struct GNUNET_ATS_Information *ats, uint32_t ats_count)
{
  struct GNUNET_TIME_Relative ret;
  int do_forward;
  struct InboundMessage *im;
  size_t msg_size = ntohs (message->size);
  size_t size =
      sizeof (struct InboundMessage) + msg_size +
      sizeof (struct GNUNET_ATS_Information) * (ats_count + 1);
  char buf[size] GNUNET_ALIGN;
  struct GNUNET_ATS_Information *ap;

  ret = GNUNET_TIME_UNIT_ZERO;
  do_forward = GNUNET_SYSERR;
  ret = GST_neighbours_calculate_receive_delay (peer, msg_size, &do_forward);

  if (!GST_neighbours_test_connected (peer))
  {

    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Discarded %u bytes type %u payload from peer `%s'\n", msg_size,
                ntohs (message->type), GNUNET_i2s (peer));

    GNUNET_STATISTICS_update (GST_stats,
                              gettext_noop
                              ("# bytes payload discarded due to not connected peer "),
                              msg_size, GNUNET_NO);
    return ret;
  }

  if (do_forward != GNUNET_YES)
    return ret;
  im = (struct InboundMessage *) buf;
  im->header.size = htons (size);
  im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
  im->ats_count = htonl (ats_count + 1);
  im->peer = *peer;
  ap = (struct GNUNET_ATS_Information *) &im[1];
  memcpy (ap, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
  ap[ats_count].type = htonl (GNUNET_ATS_QUALITY_NET_DELAY);
  ap[ats_count].value =
      htonl ((uint32_t) GST_neighbour_get_latency (peer).rel_value);
  memcpy (&ap[ats_count + 1], message, ntohs (message->size));

  GNUNET_ATS_address_update (GST_ats, address, session, ap, ats_count + 1);
  GST_clients_broadcast (&im->header, GNUNET_YES);

  return ret;
}
/**
 * We received some payload.  Prepare to pass it on to our clients.
 *
 * @param address address and (claimed) identity of the other peer
 * @param session identifier used for this session (NULL for plugins
 *                that do not offer bi-directional communication to the sender
 *                using the same "connection")
 * @param message the message to process
 * @return how long the plugin should wait until receiving more data
 */
static struct GNUNET_TIME_Relative
process_payload (const struct GNUNET_HELLO_Address *address,
                 struct GNUNET_ATS_Session *session,
                 const struct GNUNET_MessageHeader *message)
{
  struct GNUNET_TIME_Relative ret;
  int do_forward;
  struct InboundMessage *im;
  size_t msg_size = ntohs (message->size);
  size_t size = sizeof(struct InboundMessage) + msg_size;
  char buf[size] GNUNET_ALIGN;

  do_forward = GNUNET_SYSERR;
  ret = GST_neighbours_calculate_receive_delay (&address->peer,
						msg_size,
						&do_forward);
  if (! GST_neighbours_test_connected (&address->peer))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Discarded %u bytes type %u payload from peer `%s'\n",
                msg_size,
                ntohs (message->type),
                GNUNET_i2s (&address->peer));
    GNUNET_STATISTICS_update (GST_stats, gettext_noop
                              ("# bytes payload discarded due to not connected peer"),
                              msg_size,
                              GNUNET_NO);
    return ret;
  }

  if (GNUNET_YES != do_forward)
    return ret;
  im = (struct InboundMessage *) buf;
  im->header.size = htons (size);
  im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
  im->peer = address->peer;
  memcpy (&im[1], message, ntohs (message->size));
  GST_clients_broadcast (&im->header, GNUNET_YES);
  return ret;
}
/**
 * Client asked for transmission to a peer.  Process the request.
 *
 * @param cls unused
 * @param client the client
 * @param message the send message that was sent
 */
static void
clients_handle_send (void *cls,
                     struct GNUNET_SERVER_Client *client,
                     const struct GNUNET_MessageHeader *message)
{
  const struct OutboundMessage *obm;
  const struct GNUNET_MessageHeader *obmm;
  struct SendTransmitContinuationContext *stcc;
  uint16_t size;
  uint16_t msize;
  struct TransportClient *tc;

  tc = lookup_client (client);
  if (NULL == tc)
  {
    /* client asked for transmission before 'START' */
    GNUNET_break (0);
    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
    return;
  }

  size = ntohs (message->size);
  if (size <
      sizeof (struct OutboundMessage) + sizeof (struct GNUNET_MessageHeader))
  {
    GNUNET_break (0);
    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
    return;
  }
  obm = (const struct OutboundMessage *) message;
  obmm = (const struct GNUNET_MessageHeader *) &obm[1];
  msize = size - sizeof (struct OutboundMessage);
  if (msize < sizeof (struct GNUNET_MessageHeader))
  {
    GNUNET_break (0);
    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
    return;
  }

  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Received `%s' request from client with target `%4s' and first message of type %u and total size %u\n",
              "SEND",
              GNUNET_i2s (&obm->peer),
              ntohs (obmm->type),
              msize);
  if (GNUNET_NO == GST_neighbours_test_connected (&obm->peer))
  {
    /* not connected, not allowed to send; can happen due to asynchronous operations */
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Could not send message to peer `%s': not connected\n",
                GNUNET_i2s (&obm->peer));
    GNUNET_STATISTICS_update (GST_stats,
                              gettext_noop
                              ("# bytes payload dropped (other peer was not connected)"),
                              msize, GNUNET_NO);
    GNUNET_SERVER_receive_done (client, GNUNET_OK);
    return;
  }
  GNUNET_SERVER_receive_done (client, GNUNET_OK);
  stcc = GNUNET_new (struct SendTransmitContinuationContext);
  stcc->target = obm->peer;
  stcc->client = client;
  GNUNET_SERVER_client_keep (client);
  GST_manipulation_send (&obm->peer, obmm, msize,
                       GNUNET_TIME_relative_ntoh (obm->timeout),
                       &handle_send_transmit_continuation, stcc);
}