/**
 * Main point of test execution
 */
static void
run (void *cls, char *const *args, const char *cfgfile,
     const struct GNUNET_CONFIGURATION_Handle *cfg)
{
  struct TestingContext *test_ctx;
  char *emsg;
  struct GNUNET_PeerIdentity id;
  struct GNUNET_TESTING_SharedService ss[] = {
    {"peerinfo", cfg, 2},
    {NULL, NULL, 0}
  };
  struct GNUNET_TESTING_Peer *peer;
  unsigned int cnt;

  test_ctx = GNUNET_new (struct TestingContext);
  test_ctx->system =
      GNUNET_TESTING_system_create ("test-gnunet-testing",
                                    "127.0.0.1", NULL, ss);
  emsg = NULL;
  if (NULL == test_ctx->system)
    goto end;
  test_ctx->cfg = GNUNET_CONFIGURATION_dup (cfg);
  for (cnt = 0; cnt < NUM_PEERS; cnt++)
  {
    peer = GNUNET_TESTING_peer_configure (test_ctx->system,
                                          test_ctx->cfg,
                                          0, &id, &emsg);
    if (NULL == peer)
    {
      if (NULL != emsg)
        printf ("Test failed upon error: %s", emsg);
      goto end;
    }
    if (GNUNET_OK != GNUNET_TESTING_peer_start (peer))
    {
      GNUNET_TESTING_peer_destroy (peer);
      goto end;
    }
    test_ctx->peers[cnt] = peer;
  }
  status = GNUNET_OK;
  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
                                &do_shutdown, test_ctx);
  return;

 end:
  GNUNET_SCHEDULER_add_now (&do_shutdown, test_ctx);
  GNUNET_free_non_null (emsg);
}
/**
 * Start a peer with the given configuration
 * @param tth the testing handle
 * @param cfgname configuration file
 * @param peer_id a unique number to identify the peer
 * @param rec receive callback
 * @param nc connect callback
 * @param nd disconnect callback
 * @param start_cb start callback
 * @param cb_cls closure for callback
 * @return the peer context
 */
struct PeerContext *
GNUNET_TRANSPORT_TESTING_start_peer (struct GNUNET_TRANSPORT_TESTING_handle *tth,
                                     const char *cfgname, int peer_id,
                                     GNUNET_TRANSPORT_ReceiveCallback rec,
                                     GNUNET_TRANSPORT_NotifyConnect nc,
                                     GNUNET_TRANSPORT_NotifyDisconnect nd,
                                     GNUNET_TRANSPORT_TESTING_start_cb start_cb,
                                     void *cb_cls)
{
  char *emsg = NULL;
  struct GNUNET_PeerIdentity *dummy;

  GNUNET_assert (NULL != tth);
  GNUNET_assert (NULL != tth->tl_system);

  if (GNUNET_DISK_file_test (cfgname) == GNUNET_NO)
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
                     "File not found: `%s' \n", cfgname);
    return NULL;
  }

  struct PeerContext *p = GNUNET_new (struct PeerContext);
  GNUNET_CONTAINER_DLL_insert (tth->p_head, tth->p_tail, p);

  /* Create configuration and call testing lib to modify it */
  p->cfg = GNUNET_CONFIGURATION_create ();
  GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));

  if (GNUNET_SYSERR == GNUNET_TESTING_configuration_create (tth->tl_system, p->cfg))
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
                     "Testing library failed to create unique configuration based on `%s'\n",
                     cfgname);
    GNUNET_free (p);
    return NULL;
  }

  p->no = peer_id;
  /* Configure peer with configuration */
  p->peer = GNUNET_TESTING_peer_configure (tth->tl_system, p->cfg, p->no, NULL, &emsg);
  if (NULL == p->peer)
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
                     "Testing library failed to create unique configuration based on `%s': `%s'\n",
                     cfgname, emsg);
    GNUNET_TRANSPORT_TESTING_stop_peer (tth, p);
    GNUNET_free_non_null (emsg);
    return NULL;
  }
  GNUNET_free_non_null (emsg);
  if (GNUNET_OK != GNUNET_TESTING_peer_start (p->peer))
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
                     "Testing library failed to create unique configuration based on `%s'\n",
                     cfgname);
    GNUNET_TRANSPORT_TESTING_stop_peer (tth, p);
    return NULL;
  }

  memset(&dummy, '\0', sizeof (dummy));
  GNUNET_TESTING_peer_get_identity (p->peer, &p->id);
  if (0 == memcmp (&dummy, &p->id, sizeof (struct GNUNET_PeerIdentity)))
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
                     "Testing library failed to obtain peer identity for peer %u\n",
                     p->no);
    GNUNET_TRANSPORT_TESTING_stop_peer (tth, p);
    return NULL;
  }
  else
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
                     "Peer %u configured with identity `%s'\n",
                     p->no,
                     GNUNET_i2s_full (&p->id));
  }

  p->tth = tth;
  p->nc = nc;
  p->nd = nd;
  p->rec = rec;
  p->start_cb = start_cb;
  if (cb_cls != NULL)
    p->cb_cls = cb_cls;
  else
    p->cb_cls = p;

  p->th = GNUNET_TRANSPORT_connect (p->cfg, NULL, p,
                                    &notify_receive,
                                    &notify_connect, &notify_disconnect);
  if (NULL == p->th)
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
                     "Failed to connect to transport service for peer  `%s': `%s'\n",
                     cfgname, emsg);
    GNUNET_TRANSPORT_TESTING_stop_peer (tth, p);
    return NULL;
  }

  p->ghh = GNUNET_TRANSPORT_get_hello (p->th, &get_hello, p);
  GNUNET_assert (p->ghh != NULL);

  return p;
}
/**
 * Handler for #GNUNET_MESSAGE_TYPE_TESTBED_CREATEPEER messages
 *
 * @param cls identification of the client
 * @param msg the actual message
 */
void
handle_peer_create (void *cls,
                    const struct GNUNET_TESTBED_PeerCreateMessage *msg)
{
  struct GNUNET_SERVICE_Client *client = cls;
  struct GNUNET_MQ_Envelope *env;
  struct GNUNET_TESTBED_PeerCreateSuccessEventMessage *reply;
  struct GNUNET_CONFIGURATION_Handle *cfg;
  struct ForwardedOperationContext *fo_ctxt;
  struct Route *route;
  struct Peer *peer;
  char *emsg;
  uint32_t host_id;
  uint32_t peer_id;

  host_id = ntohl (msg->host_id);
  peer_id = ntohl (msg->peer_id);
  if (VALID_PEER_ID (peer_id))
  {
    (void) GNUNET_asprintf (&emsg,
                            "Peer with ID %u already exists",
                            peer_id);
    GST_send_operation_fail_msg (client,
                                 GNUNET_ntohll (msg->operation_id),
                                 emsg);
    GNUNET_free (emsg);
    GNUNET_SERVICE_client_continue (client);
    return;
  }
  if (UINT32_MAX == peer_id)
  {
    GST_send_operation_fail_msg (client,
                                 GNUNET_ntohll (msg->operation_id),
                                 "Cannot create peer with given ID");
    GNUNET_SERVICE_client_continue (client);
    return;
  }
  if (host_id == GST_context->host_id)
  {
    /* We are responsible for this peer */
    cfg = GNUNET_TESTBED_extract_config_ (&msg->header);
    if (NULL == cfg)
    {
      GNUNET_break (0);
      GNUNET_SERVICE_client_drop (client);
      return;
    }
    GNUNET_CONFIGURATION_set_value_number (cfg,
                                           "TESTBED",
                                           "PEERID",
                                           (unsigned long long) peer_id);

    GNUNET_CONFIGURATION_set_value_number (cfg,
                                           "PATHS",
                                           "PEERID",
                                           (unsigned long long) peer_id);
    peer = GNUNET_new (struct Peer);
    peer->is_remote = GNUNET_NO;
    peer->details.local.cfg = cfg;
    peer->id = peer_id;
    LOG_DEBUG ("Creating peer with id: %u\n",
               (unsigned int) peer->id);
    peer->details.local.peer =
        GNUNET_TESTING_peer_configure (GST_context->system,
                                       peer->details.local.cfg, peer->id,
                                       NULL /* Peer id */ ,
                                       &emsg);
    if (NULL == peer->details.local.peer)
    {
      LOG (GNUNET_ERROR_TYPE_WARNING,
           "Configuring peer failed: %s\n",
           emsg);
      GNUNET_free (emsg);
      GNUNET_free (peer);
      GNUNET_break (0);
      GNUNET_SERVICE_client_drop (client);
      return;
    }
    peer->details.local.is_running = GNUNET_NO;
    peer_list_add (peer);
    env = GNUNET_MQ_msg (reply,
                         GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER_SUCCESS);
    reply->peer_id = msg->peer_id;
    reply->operation_id = msg->operation_id;
    GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
                    env);
    GNUNET_SERVICE_client_continue (client);
    return;
  }