/**
 * Iterate over routing table and remove entries with value as part of any trail.
 *
 * @param cls closure
 * @param key current public key
 * @param value value in the hash map
 * @return #GNUNET_YES if we should continue to iterate,
 *         #GNUNET_NO if not.
 */
static int
remove_matching_trails (void *cls,
			const struct GNUNET_HashCode *key,
			void *value)
{
  struct RoutingTrail *remove_trail = value;
  struct GNUNET_PeerIdentity *disconnected_peer = cls;
  struct GNUNET_HashCode trail_id = *key;
  struct GNUNET_PeerIdentity my_identity;

  /* If disconnected_peer is next_hop, then send a trail teardown message through
   * prev_hop in direction from destination to source. */
  if (0 == GNUNET_CRYPTO_cmp_peer_identity (&remove_trail->next_hop,
                                            disconnected_peer))
  {
    my_identity = *GDS_NEIGHBOURS_get_id ();
    if (0 != GNUNET_CRYPTO_cmp_peer_identity (&my_identity,
                                              &remove_trail->prev_hop))
    {
      GDS_NEIGHBOURS_send_trail_teardown (&trail_id,
                                          GDS_ROUTING_DEST_TO_SRC,
                                          &remove_trail->prev_hop);
    }
  }

  /* If disconnected_peer is prev_hop, then send a trail teardown through
   * next_hop in direction from Source to Destination. */
  if (0 == GNUNET_CRYPTO_cmp_peer_identity (&remove_trail->prev_hop,
                                            disconnected_peer))
  {
    my_identity = *GDS_NEIGHBOURS_get_id ();

    if (0 != GNUNET_CRYPTO_cmp_peer_identity (&my_identity,
                                              &remove_trail->next_hop))
    {
      GDS_NEIGHBOURS_send_trail_teardown (&trail_id,
                                          GDS_ROUTING_SRC_TO_DEST,
                                          &remove_trail->next_hop);
    }
  }

  GNUNET_assert (GNUNET_YES ==
                   GNUNET_CONTAINER_multihashmap_remove (routing_table,
                                                         &trail_id,
                                                         remove_trail));
  GNUNET_free (remove_trail);
  return GNUNET_YES;
}
/**
 * TEST FUNCTION
 * Remove after using.
 */
void
GDS_ROUTING_test_print (void)
{
  struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
  struct RoutingTrail *trail;
  struct GNUNET_PeerIdentity print_peer;
  struct GNUNET_HashCode key_ret;
  int i;

  struct GNUNET_PeerIdentity my_identity = *GDS_NEIGHBOURS_get_id();
  print_peer = my_identity;
   FPRINTF (stderr,_("\nSUPU ***PRINTING ROUTING TABLE ***** of =%s"),GNUNET_i2s(&print_peer));
  iter =GNUNET_CONTAINER_multihashmap_iterator_create (routing_table);
  for (i = 0; i < GNUNET_CONTAINER_multihashmap_size(routing_table); i++)
  {
    if(GNUNET_YES == GNUNET_CONTAINER_multihashmap_iterator_next (iter,
                                                                  &key_ret,
                                                                  (const void **)&trail))
    {
      FPRINTF (stderr,_("\nSUPU %s, %s, %d, trail->trail_id = %s"),
              __FILE__, __func__,__LINE__, GNUNET_h2s(&trail->trail_id));
      GNUNET_memcpy (&print_peer, &trail->next_hop, sizeof (struct GNUNET_PeerIdentity));
      FPRINTF (stderr,_("\nSUPU %s, %s, %d, trail->next_hop = %s"),
              __FILE__, __func__,__LINE__, GNUNET_i2s(&print_peer));
      GNUNET_memcpy (&print_peer, &trail->prev_hop, sizeof (struct GNUNET_PeerIdentity));
      FPRINTF (stderr,_("\nSUPU %s, %s, %d, trail->prev_hop = %s"),
              __FILE__, __func__,__LINE__, GNUNET_i2s(&print_peer));
    }
  }
}
/**
 * Handler for DHT GET messages from the client.
 *
 * @param cls closure for the service
 * @param client the client we received this message from
 * @param message the actual message received
 */
static void
handle_dht_local_get (void *cls, struct GNUNET_SERVER_Client *client,
                      const struct GNUNET_MessageHeader *message)
{
  const struct GNUNET_DHT_ClientGetMessage *get;
  struct ClientQueryRecord *cqr;
  size_t xquery_size;
  const char *xquery;
  uint16_t size;

  size = ntohs (message->size);
  if (size < sizeof (struct GNUNET_DHT_ClientGetMessage))
  {
    GNUNET_break (0);
    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
    return;
  }
  xquery_size = size - sizeof (struct GNUNET_DHT_ClientGetMessage);
  get = (const struct GNUNET_DHT_ClientGetMessage *) message;
  xquery = (const char *) &get[1];
  GNUNET_STATISTICS_update (GDS_stats,
                            gettext_noop
                            ("# GET requests received from clients"), 1,
                            GNUNET_NO);
  LOG (GNUNET_ERROR_TYPE_DEBUG,
       "Received GET request for %s from local client %p, xq: %.*s\n",
       GNUNET_h2s (&get->key), client, xquery_size, xquery);

  LOG_TRAFFIC (GNUNET_ERROR_TYPE_DEBUG, "XDHT CLIENT-GET %s @ %u\n",
               GNUNET_h2s (&get->key), getpid ());


  cqr = GNUNET_malloc (sizeof (struct ClientQueryRecord) + xquery_size);
  cqr->key = get->key;
  cqr->client = find_active_client (client);
  cqr->xquery = (void *) &cqr[1];
  memcpy (&cqr[1], xquery, xquery_size);
  cqr->hnode = GNUNET_CONTAINER_heap_insert (retry_heap, cqr, 0);
  cqr->retry_frequency = GNUNET_TIME_UNIT_SECONDS;
  cqr->retry_time = GNUNET_TIME_absolute_get ();
  cqr->unique_id = get->unique_id;
  cqr->xquery_size = xquery_size;
  cqr->replication = ntohl (get->desired_replication_level);
  cqr->msg_options = ntohl (get->options);
  cqr->type = ntohl (get->type);
  // FIXME use cqr->key, set multihashmap create to GNUNET_YES
  GNUNET_CONTAINER_multihashmap_put (forward_map, &get->key, cqr,
                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
  GDS_CLIENTS_process_get (ntohl (get->options),
                           ntohl (get->type),
                           0,
                           ntohl (get->desired_replication_level),
                           1,
                           GDS_NEIGHBOURS_get_id(),
                           &get->key);
  /* start remote requests */
  if (GNUNET_SCHEDULER_NO_TASK != retry_task)
    GNUNET_SCHEDULER_cancel (retry_task);
  retry_task = GNUNET_SCHEDULER_add_now (&transmit_next_request_task, NULL);
  /* perform local lookup */
  GDS_DATACACHE_handle_get (&get->key, cqr->type, cqr->xquery, xquery_size,
                            NULL, 0);
  GNUNET_SERVER_receive_done (client, GNUNET_OK);
}
/**
 * Handler for PUT messages.
 *
 * @param cls closure for the service
 * @param client the client we received this message from
 * @param message the actual message received
 */
static void
handle_dht_local_put (void *cls, struct GNUNET_SERVER_Client *client,
                      const struct GNUNET_MessageHeader *message)
{
  const struct GNUNET_DHT_ClientPutMessage *dht_msg;
  struct GNUNET_CONTAINER_BloomFilter *peer_bf;
  uint16_t size;
  struct PendingMessage *pm;
  struct GNUNET_DHT_ClientPutConfirmationMessage *conf;

  size = ntohs (message->size);
  if (size < sizeof (struct GNUNET_DHT_ClientPutMessage))
  {
    GNUNET_break (0);
    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
    return;
  }
  GNUNET_STATISTICS_update (GDS_stats,
                            gettext_noop
                            ("# PUT requests received from clients"), 1,
                            GNUNET_NO);
  dht_msg = (const struct GNUNET_DHT_ClientPutMessage *) message;
  LOG_TRAFFIC (GNUNET_ERROR_TYPE_DEBUG, "XDHT CLIENT-PUT %s @ %u\n",
               GNUNET_h2s (&dht_msg->key), getpid ());
  /* give to local clients */
  LOG (GNUNET_ERROR_TYPE_DEBUG,
       "Handling local PUT of %u-bytes for query %s\n",
       size - sizeof (struct GNUNET_DHT_ClientPutMessage),
       GNUNET_h2s (&dht_msg->key));
  GDS_CLIENTS_handle_reply (GNUNET_TIME_absolute_ntoh (dht_msg->expiration),
                            &dht_msg->key, 0, NULL, 0, NULL,
                            ntohl (dht_msg->type),
                            size - sizeof (struct GNUNET_DHT_ClientPutMessage),
                            &dht_msg[1]);
  /* store locally */
  GDS_DATACACHE_handle_put (GNUNET_TIME_absolute_ntoh (dht_msg->expiration),
                            &dht_msg->key, 0, NULL, ntohl (dht_msg->type),
                            size - sizeof (struct GNUNET_DHT_ClientPutMessage),
                            &dht_msg[1]);
  /* route to other peers */
  peer_bf =
      GNUNET_CONTAINER_bloomfilter_init (NULL, DHT_BLOOM_SIZE,
                                         GNUNET_CONSTANTS_BLOOMFILTER_K);

  GDS_NEIGHBOURS_handle_put (ntohl (dht_msg->type), ntohl (dht_msg->options),
                             ntohl (dht_msg->desired_replication_level),
                             GNUNET_TIME_absolute_ntoh (dht_msg->expiration),
                             0 /* hop count */ ,
                             peer_bf, &dht_msg->key, 0, NULL, &dht_msg[1],
                             size -
                             sizeof (struct GNUNET_DHT_ClientPutMessage));
  GDS_CLIENTS_process_put (ntohl (dht_msg->options),
                           ntohl (dht_msg->type),
                           0,
                           ntohl (dht_msg->desired_replication_level),
                           1,
                           GDS_NEIGHBOURS_get_id(),
                           GNUNET_TIME_absolute_ntoh (dht_msg->expiration),
                           &dht_msg->key,
                           &dht_msg[1],
                           size - sizeof (struct GNUNET_DHT_ClientPutMessage));
  GNUNET_CONTAINER_bloomfilter_free (peer_bf);
  pm = GNUNET_malloc (sizeof (struct PendingMessage) +
		      sizeof (struct GNUNET_DHT_ClientPutConfirmationMessage));
  conf = (struct GNUNET_DHT_ClientPutConfirmationMessage *) &pm[1];
  conf->header.size = htons (sizeof (struct GNUNET_DHT_ClientPutConfirmationMessage));
  conf->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_CLIENT_PUT_OK);
  conf->reserved = htonl (0);
  conf->unique_id = dht_msg->unique_id;
  pm->msg = &conf->header;
  add_pending_message (find_active_client (client), pm);
  GNUNET_SERVER_receive_done (client, GNUNET_OK);
}