/**
 * Iterator for local get request results,
 *
 * @param cls closure for iterator, a DatacacheGetContext
 * @param exp when does this value expire?
 * @param key the key this data is stored under
 * @param size the size of the data identified by key
 * @param data the actual data
 * @param type the type of the data
 * @param put_path_length number of peers in 'put_path'
 * @param put_path path the reply took on put
 * @return GNUNET_OK to continue iteration, anything else
 * to stop iteration.
 */
static int
datacache_get_iterator (void *cls, 
                        const struct GNUNET_HashCode * key, size_t size,
                        const char *data, enum GNUNET_BLOCK_Type type,
			struct GNUNET_TIME_Absolute exp,
			unsigned int put_path_length,
			const struct GNUNET_PeerIdentity *put_path)
{
  struct GetRequestContext *ctx = cls;
  enum GNUNET_BLOCK_EvaluationResult eval;

  eval =
      GNUNET_BLOCK_evaluate (GDS_block_context, type, key, ctx->reply_bf,
                             ctx->reply_bf_mutator, ctx->xquery,
                             ctx->xquery_size, data, size);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Found reply for query %s in datacache, evaluation result is %d\n",
              GNUNET_h2s (key), (int) eval);
  ctx->eval = eval;
  switch (eval)
  {
  case GNUNET_BLOCK_EVALUATION_OK_LAST:
  case GNUNET_BLOCK_EVALUATION_OK_MORE:
    /* forward to local clients */
    GNUNET_STATISTICS_update (GDS_stats,
                              gettext_noop
                              ("# Good RESULTS found in datacache"), 1,
                              GNUNET_NO);
    GDS_CLIENTS_handle_reply (exp, key, 0, NULL, put_path_length, put_path, type,
                              size, data);
    /* forward to other peers */
    GDS_ROUTING_process (type, exp, key, put_path_length, put_path, 0, NULL, data,
                         size);
    break;
  case GNUNET_BLOCK_EVALUATION_OK_DUPLICATE:
    GNUNET_STATISTICS_update (GDS_stats,
                              gettext_noop
                              ("# Duplicate RESULTS found in datacache"), 1,
                              GNUNET_NO);
    break;
  case GNUNET_BLOCK_EVALUATION_RESULT_INVALID:
    GNUNET_STATISTICS_update (GDS_stats,
                              gettext_noop
                              ("# Invalid RESULTS found in datacache"), 1,
                              GNUNET_NO);
    break;
  case GNUNET_BLOCK_EVALUATION_RESULT_IRRELEVANT:
    GNUNET_STATISTICS_update (GDS_stats,
                              gettext_noop
                              ("# Irrelevant RESULTS found in datacache"), 1,
                              GNUNET_NO);
    break;
  case GNUNET_BLOCK_EVALUATION_REQUEST_VALID:
    GNUNET_break (0);
    break;
  case GNUNET_BLOCK_EVALUATION_REQUEST_INVALID:
    GNUNET_break_op (0);
    return GNUNET_SYSERR;
  case GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED:
    GNUNET_STATISTICS_update (GDS_stats,
                              gettext_noop
                              ("# Unsupported RESULTS found in datacache"), 1,
                              GNUNET_NO);
    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                _("Unsupported block type (%u) in local response!\n"), type);
    break;
  }
  return (eval == GNUNET_BLOCK_EVALUATION_OK_LAST) ? GNUNET_NO : GNUNET_OK;
}
/**
 * Iterator over hash map entries that send a given reply to
 * each of the matching clients.  With some tricky recycling
 * of the buffer.
 *
 * @param cls the 'struct ForwardReplyContext'
 * @param key current key
 * @param value value in the hash map, a ClientQueryRecord
 * @return GNUNET_YES (we should continue to iterate),
 *         if the result is mal-formed, GNUNET_NO
 */
static int
forward_reply (void *cls, const struct GNUNET_HashCode * key, void *value)
{
  struct ForwardReplyContext *frc = cls;
  struct ClientQueryRecord *record = value;
  struct PendingMessage *pm;
  struct GNUNET_DHT_ClientResultMessage *reply;
  enum GNUNET_BLOCK_EvaluationResult eval;
  int do_free;
  struct GNUNET_HashCode ch;
  unsigned int i;

  LOG_TRAFFIC (GNUNET_ERROR_TYPE_DEBUG,
	       "XDHT CLIENT-RESULT %s\n",
               GNUNET_h2s (key));
  if ((record->type != GNUNET_BLOCK_TYPE_ANY) && (record->type != frc->type))
  {
    LOG (GNUNET_ERROR_TYPE_DEBUG,
         "Record type missmatch, not passing request for key %s to local client\n",
         GNUNET_h2s (key));
    GNUNET_STATISTICS_update (GDS_stats,
                              gettext_noop
                              ("# Key match, type mismatches in REPLY to CLIENT"),
                              1, GNUNET_NO);
    return GNUNET_YES;          /* type mismatch */
  }
  GNUNET_CRYPTO_hash (frc->data, frc->data_size, &ch);
  for (i = 0; i < record->seen_replies_count; i++)
    if (0 == memcmp (&record->seen_replies[i], &ch, sizeof (struct GNUNET_HashCode)))
    {
      LOG (GNUNET_ERROR_TYPE_DEBUG,
           "Duplicate reply, not passing request for key %s to local client\n",
           GNUNET_h2s (key));
      GNUNET_STATISTICS_update (GDS_stats,
                                gettext_noop
                                ("# Duplicate REPLIES to CLIENT request dropped"),
                                1, GNUNET_NO);
      return GNUNET_YES;        /* duplicate */
    }
  eval =
      GNUNET_BLOCK_evaluate (GDS_block_context, record->type, key, NULL, 0,
                             record->xquery, record->xquery_size, frc->data,
                             frc->data_size);
  LOG (GNUNET_ERROR_TYPE_DEBUG,
       "Evaluation result is %d for key %s for local client's query\n",
       (int) eval, GNUNET_h2s (key));
  switch (eval)
  {
  case GNUNET_BLOCK_EVALUATION_OK_LAST:
    do_free = GNUNET_YES;
    break;
  case GNUNET_BLOCK_EVALUATION_OK_MORE:
    GNUNET_array_append (record->seen_replies, record->seen_replies_count, ch);
    do_free = GNUNET_NO;
    break;
  case GNUNET_BLOCK_EVALUATION_OK_DUPLICATE:
    /* should be impossible to encounter here */
    GNUNET_break (0);
    return GNUNET_YES;
  case GNUNET_BLOCK_EVALUATION_RESULT_INVALID:
    GNUNET_break_op (0);
    return GNUNET_NO;
  case GNUNET_BLOCK_EVALUATION_REQUEST_VALID:
    GNUNET_break (0);
    return GNUNET_NO;
  case GNUNET_BLOCK_EVALUATION_REQUEST_INVALID:
    GNUNET_break (0);
    return GNUNET_NO;
  case GNUNET_BLOCK_EVALUATION_RESULT_IRRELEVANT:
    return GNUNET_YES;
  case GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED:
    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                _("Unsupported block type (%u) in request!\n"), record->type);
    return GNUNET_NO;
  default:
    GNUNET_break (0);
    return GNUNET_NO;
  }
  if (GNUNET_NO == frc->do_copy)
  {
    /* first time, we can use the original data */
    pm = frc->pm;
    frc->do_copy = GNUNET_YES;
  }
  else
  {
    /* two clients waiting for same reply, must copy for queueing */
    pm = GNUNET_malloc (sizeof (struct PendingMessage) +
                        ntohs (frc->pm->msg->size));
    memcpy (pm, frc->pm,
            sizeof (struct PendingMessage) + ntohs (frc->pm->msg->size));
    pm->next = pm->prev = NULL;
    pm->msg = (struct GNUNET_MessageHeader *) &pm[1];
  }
  GNUNET_STATISTICS_update (GDS_stats,
                            gettext_noop ("# RESULTS queued for clients"), 1,
                            GNUNET_NO);
  reply = (struct GNUNET_DHT_ClientResultMessage *) &pm[1];
  reply->unique_id = record->unique_id;
  LOG (GNUNET_ERROR_TYPE_DEBUG,
       "Queueing reply to query %s for client %p\n",
       GNUNET_h2s (key),
       record->client->client_handle);
  add_pending_message (record->client, pm);
  if (GNUNET_YES == do_free)
    remove_client_records (record->client, key, record);
  return GNUNET_YES;
}
/**
 * Forward the result to the given peer if it matches the request.
 *
 * @param cls the 'struct ProcessContext' with the result
 * @param key the query
 * @param value the 'struct RecentRequest' with the request
 * @return GNUNET_OK (continue to iterate),
 *         GNUNET_SYSERR if the result is malformed or type unsupported
 */
static int
process (void *cls, const struct GNUNET_HashCode * key, void *value)
{
  struct ProcessContext *pc = cls;
  struct RecentRequest *rr = value;
  enum GNUNET_BLOCK_EvaluationResult eval;
  unsigned int gpl;
  unsigned int ppl;
  struct GNUNET_HashCode hc;
  const struct GNUNET_HashCode *eval_key;

  if ((rr->type != GNUNET_BLOCK_TYPE_ANY) && (rr->type != pc->type))
    return GNUNET_OK;           /* type missmatch */

  if (0 != (rr->options & GNUNET_DHT_RO_RECORD_ROUTE))
  {
    gpl = pc->get_path_length;
    ppl = pc->put_path_length;
  }
  else
  {
    gpl = 0;
    ppl = 0;
  }
  if ((0 != (rr->options & GNUNET_DHT_RO_FIND_PEER)) &&
      (pc->type == GNUNET_BLOCK_TYPE_DHT_HELLO))
  {
    /* key may not match HELLO, which is OK since
     * the search is approximate.  Still, the evaluation
     * would fail since the match is not exact.  So
     * we fake it by changing the key to the actual PID ... */
    GNUNET_BLOCK_get_key (GDS_block_context, GNUNET_BLOCK_TYPE_DHT_HELLO,
                          pc->data, pc->data_size, &hc);
    eval_key = &hc;
  }
  else
  {
    eval_key = key;
  }
  eval =
      GNUNET_BLOCK_evaluate (GDS_block_context, pc->type, eval_key,
                             &rr->reply_bf, rr->reply_bf_mutator, rr->xquery,
                             rr->xquery_size, pc->data, pc->data_size);
  switch (eval)
  {
  case GNUNET_BLOCK_EVALUATION_OK_MORE:
  case GNUNET_BLOCK_EVALUATION_OK_LAST:
    GNUNET_STATISTICS_update (GDS_stats,
                              gettext_noop
                              ("# Good REPLIES matched against routing table"),
                              1, GNUNET_NO);
    GDS_NEIGHBOURS_handle_reply (&rr->peer, pc->type, pc->expiration_time, key,
                                 ppl, pc->put_path, gpl, pc->get_path, pc->data,
                                 pc->data_size);
    break;
  case GNUNET_BLOCK_EVALUATION_OK_DUPLICATE:
    GNUNET_STATISTICS_update (GDS_stats,
                              gettext_noop
                              ("# Duplicate REPLIES matched against routing table"),
                              1, GNUNET_NO);
    return GNUNET_OK;
  case GNUNET_BLOCK_EVALUATION_RESULT_INVALID:
    GNUNET_STATISTICS_update (GDS_stats,
                              gettext_noop
                              ("# Invalid REPLIES matched against routing table"),
                              1, GNUNET_NO);
    return GNUNET_SYSERR;
  case GNUNET_BLOCK_EVALUATION_RESULT_IRRELEVANT:
    GNUNET_STATISTICS_update (GDS_stats,
                              gettext_noop
                              ("# Irrelevant REPLIES matched against routing table"),
                              1, GNUNET_NO);
    return GNUNET_OK;
  case GNUNET_BLOCK_EVALUATION_REQUEST_VALID:
    GNUNET_break (0);
    return GNUNET_OK;
  case GNUNET_BLOCK_EVALUATION_REQUEST_INVALID:
    GNUNET_break (0);
    return GNUNET_OK;
  case GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED:
    GNUNET_STATISTICS_update (GDS_stats,
                              gettext_noop
                              ("# Unsupported REPLIES matched against routing table"),
                              1, GNUNET_NO);
    return GNUNET_SYSERR;
  default:
    GNUNET_break (0);
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}