/**
 * Adapter function called to establish a connection to
 * a service.
 * 
 * @param cls closure
 * @param cfg configuration of the peer to connect to; will be available until
 *          GNUNET_TESTBED_operation_done() is called on the operation returned
 *          from GNUNET_TESTBED_service_connect()
 * @return service handle to return in 'op_result', NULL on error
 */
static void * 
stream_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
{
  struct PeerData *pdata = cls;

  if (&peer_data[1] == pdata)
  {
    peer2_listen_socket = NULL;
    peer2_listen_socket =
	GNUNET_STREAM_listen (cfg, 10, &stream_listen_cb, &peer_data[1],
			      GNUNET_STREAM_OPTION_SIGNAL_LISTEN_SUCCESS,
			      &stream_connect2,
                              GNUNET_STREAM_OPTION_MAX_PAYLOAD_SIZE,
                              payload_size[payload_size_index],
                              GNUNET_STREAM_OPTION_END);
    GNUNET_assert (NULL != peer2_listen_socket);
    return peer2_listen_socket;
  }
  if (&peer_data[0] == pdata)
  {
    pdata->socket =
	GNUNET_STREAM_open (cfg, &peer_data[1].id, 10, &stream_open_cb,
			    &peer_data[0],
			    GNUNET_STREAM_OPTION_MAX_PAYLOAD_SIZE,
			    payload_size[payload_size_index],
			    GNUNET_STREAM_OPTION_END);
    GNUNET_assert (NULL != pdata->socket);
    return pdata->socket;
  }
  GNUNET_assert (0);
  return NULL;
}
/**
 * Testing function
 *
 * @param cls NULL
 * @param tc the task context
 */
static void
test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
  struct GNUNET_PeerIdentity self;

  test_task = GNUNET_SCHEDULER_NO_TASK;
  /* Get our identity */
  GNUNET_assert (GNUNET_OK == GNUNET_TESTING_get_peer_identity (config_peer1,
                                                                &self));

  peer2_listen_socket = GNUNET_STREAM_listen (config_peer2,
                                              10, /* App port */
                                              &stream_listen_cb,
                                              &peer2);
  GNUNET_assert (NULL != peer2_listen_socket);

  /* Connect to stream library */
  peer1.socket = GNUNET_STREAM_open (config_peer1,
                                     &self,         /* Null for local peer? */
                                     10,           /* App port */
                                     &stream_open_cb,
                                     &peer1,
				     GNUNET_STREAM_OPTION_END);
  GNUNET_assert (NULL != peer1.socket);
}
/**
 * Listen success callback; connects a peer to stream as client
 */
static void
stream_connect (void)
{
  peer_data[0].socket = 
      GNUNET_STREAM_open (config, &peer_data[1].id, 10, &stream_open_cb,
			  &peer_data[0],
			  GNUNET_STREAM_OPTION_MAX_PAYLOAD_SIZE,
			  payload_size[payload_size_index],
			  GNUNET_STREAM_OPTION_END);
  GNUNET_assert (NULL != peer_data[0].socket);
}
/**
 * Listen success callback; connects a peer to stream as client
 */
static void
stream_connect (void)
{
    struct PeerData *peer = &peer1;

    /* Connect to stream */
    peer->socket = GNUNET_STREAM_open (config,
                                       &peer2.self,         /* Null for local peer? */
                                       10,           /* App port */
                                       &stream_open_cb, &peer1,
                                       GNUNET_STREAM_OPTION_MAX_PAYLOAD_SIZE, 500,
                                       GNUNET_STREAM_OPTION_END);
    GNUNET_assert (NULL != peer->socket);
}
/**
 * We had a serious error, tear down and re-create stream from scratch.
 *
 * @param sh stream to reset
 */
static void
reset_stream (struct StreamHandle *sh)
{
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
	      "Resetting stream to %s\n",
	      GNUNET_i2s (&sh->target));
  if (NULL != sh->rh)
    GNUNET_STREAM_io_read_cancel (sh->rh);
  GNUNET_STREAM_close (sh->stream);
  sh->is_ready = GNUNET_NO;
  GNUNET_CONTAINER_multihashmap_iterate (sh->waiting_map,
					 &move_to_pending,
					 sh);
  sh->stream = GNUNET_STREAM_open (GSF_cfg,
				   &sh->target,
				   GNUNET_APPLICATION_TYPE_FS_BLOCK_TRANSFER,
				   &stream_ready_cb, sh,
				   GNUNET_STREAM_OPTION_END);
}
/**
 * Get (or create) a stream to talk to the given peer.
 *
 * @param target peer we want to communicate with
 */
static struct StreamHandle *
get_stream (const struct GNUNET_PeerIdentity *target)
{
  struct StreamHandle *sh;

  sh = GNUNET_CONTAINER_multihashmap_get (stream_map,
					  &target->hashPubKey);
  if (NULL != sh)
  {
    if (GNUNET_SCHEDULER_NO_TASK != sh->timeout_task)
    {
      GNUNET_SCHEDULER_cancel (sh->timeout_task);
      sh->timeout_task = GNUNET_SCHEDULER_NO_TASK;
    }
    return sh;
  }
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
	      "Creating stream to %s\n",
	      GNUNET_i2s (target));
  sh = GNUNET_malloc (sizeof (struct StreamHandle));
  sh->mst = GNUNET_SERVER_mst_create (&reply_cb,
				      sh);
  sh->waiting_map = GNUNET_CONTAINER_multihashmap_create (512, GNUNET_YES);
  sh->target = *target;
  sh->stream = GNUNET_STREAM_open (GSF_cfg,
				   &sh->target,
				   GNUNET_APPLICATION_TYPE_FS_BLOCK_TRANSFER,
				   &stream_ready_cb, sh,
				   GNUNET_STREAM_OPTION_END);
  GNUNET_assert (GNUNET_OK ==
		 GNUNET_CONTAINER_multihashmap_put (stream_map,
						    &sh->target.hashPubKey,
						    sh,
						    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
  return sh;
}