/**
 * Both peers must have been started before calling this function.
 * This function then obtains a HELLO from 'p1', gives it to 'p2'
 * and asks 'p2' to connect to 'p1'.
 *
 * @param op_cls closure argument to give with the operation event
 * @param cb the callback to call when this operation has finished
 * @param cb_cls the closure for the above callback
 * @param p1 first peer
 * @param p2 second peer
 * @return handle to the operation, NULL if connecting these two
 *         peers is fundamentally not possible at this time (peers
 *         not running or underlay disallows)
 */
struct GNUNET_TESTBED_Operation *
GNUNET_TESTBED_overlay_connect (void *op_cls,
                                GNUNET_TESTBED_OperationCompletionCallback cb,
                                void *cb_cls,
				struct GNUNET_TESTBED_Peer *p1,
				struct GNUNET_TESTBED_Peer *p2)
{
  struct OperationContext *opc;
  struct OverlayConnectData *data;

  GNUNET_assert ((PS_STARTED == p1->state) && (PS_STARTED == p2->state));
  data = GNUNET_malloc (sizeof (struct OverlayConnectData));
  data->p1 = p1;
  data->p2 = p2;
  data->cb = cb;
  data->cb_cls = cb_cls;
  data->state = OCD_INIT;
  opc = GNUNET_malloc (sizeof (struct OperationContext));
  opc->data = data;
  opc->c = p1->controller;
  opc->id = GNUNET_TESTBED_get_next_op_id (opc->c);
  opc->type = OP_OVERLAY_CONNECT;
  opc->op_cls = op_cls;
  opc->op =
      GNUNET_TESTBED_operation_create_ (opc, &opstart_overlay_connect,
                                        &oprelease_overlay_connect);
  GNUNET_TESTBED_operation_queue_insert_
      (opc->c->opq_parallel_overlay_connect_operations, opc->op);
  GNUNET_TESTBED_operation_begin_wait_ (opc->op);
  return opc->op;
}
/**
 * Request information about a peer. The controller callback will not be called
 * with event type GNUNET_TESTBED_ET_OPERATION_FINISHED when result for this
 * operation is available. Instead, the GNUNET_TESTBED_PeerInfoCallback() will
 * be called.
 *
 * @param peer peer to request information about
 * @param pit desired information
 * @param cb the convenience callback to be called when results for this
 *          operation are available
 * @param cb_cls the closure for the above callback
 * @return handle to the operation
 */
struct GNUNET_TESTBED_Operation *
GNUNET_TESTBED_peer_get_information (struct GNUNET_TESTBED_Peer *peer,
				     enum GNUNET_TESTBED_PeerInformationType
				     pit,
				     GNUNET_TESTBED_PeerInfoCallback cb,
				     void *cb_cls)
{
  struct OperationContext *opc;
  struct PeerInfoData *data;

  GNUNET_assert (GNUNET_TESTBED_PIT_GENERIC != pit);
  data = GNUNET_malloc (sizeof (struct PeerInfoData));
  data->peer = peer;
  data->pit = pit;
  data->cb = cb;
  data->cb_cls = cb_cls;
  opc = GNUNET_malloc (sizeof (struct OperationContext));
  opc->c = peer->controller;
  opc->data = data;
  opc->type = OP_PEER_INFO;
  opc->id = GNUNET_TESTBED_get_next_op_id (opc->c);
  opc->op =
      GNUNET_TESTBED_operation_create_ (opc, &opstart_peer_getinfo,
                                        &oprelease_peer_getinfo);
  GNUNET_TESTBED_operation_queue_insert_ (opc->c->opq_parallel_operations,
                                          opc->op);
  GNUNET_TESTBED_operation_begin_wait_ (opc->op);
  return opc->op;
}
/**
 * Stop the given peer.  The handle remains valid (use
 * "GNUNET_TESTBED_peer_destroy" to fully clean up the
 * state of the peer).
 *
 * @param peer peer to stop
 * @param pcc function to call upon completion
 * @param pcc_cls closure for 'pcc'
 * @return handle to the operation
 */
struct GNUNET_TESTBED_Operation *
GNUNET_TESTBED_peer_stop (struct GNUNET_TESTBED_Peer *peer,
			  GNUNET_TESTBED_PeerChurnCallback pcc,
			  void *pcc_cls)
{
  struct OperationContext *opc;
  struct PeerEventData *data;
 
  data = GNUNET_malloc (sizeof (struct PeerEventData));
  data->peer = peer;
  data->pcc = pcc;
  data->pcc_cls = pcc_cls;
  opc = GNUNET_malloc (sizeof (struct OperationContext));
  opc->c = peer->controller;
  opc->data = data;
  opc->id = GNUNET_TESTBED_get_next_op_id (opc->c);
  opc->type = OP_PEER_STOP;
  opc->op =
      GNUNET_TESTBED_operation_create_ (opc, &opstart_peer_stop,
                                        &oprelease_peer_stop);
  GNUNET_TESTBED_operation_queue_insert_ (opc->c->opq_parallel_operations,
                                          opc->op);
  GNUNET_TESTBED_operation_begin_wait_ (opc->op);
  return opc->op;
}
/**
 * Destroy the given peer; the peer should have been
 * stopped first (if it was started).
 *
 * @param peer peer to stop
 * @return handle to the operation
 */
struct GNUNET_TESTBED_Operation *
GNUNET_TESTBED_peer_destroy (struct GNUNET_TESTBED_Peer *peer)
{
  struct OperationContext *opc;

  opc = GNUNET_malloc (sizeof (struct OperationContext));
  opc->data = peer;
  opc->c = peer->controller;
  opc->id = GNUNET_TESTBED_get_next_op_id (peer->controller);
  opc->type = OP_PEER_DESTROY;
  opc->op =
      GNUNET_TESTBED_operation_create_ (opc, &opstart_peer_destroy,
                                        &oprelease_peer_destroy);
  GNUNET_TESTBED_operation_queue_insert_ (opc->c->opq_parallel_operations,
                                          opc->op);
  GNUNET_TESTBED_operation_begin_wait_ (opc->op);
  return opc->op;
}
/**
 * Function called when a service connect operation is ready
 *
 * @param cls the closure from GNUNET_TESTBED_operation_create_()
 */
static void
opstart_service_connect (void *cls)
{
  struct ServiceConnectData *data = cls;
  struct GNUNET_TESTBED_PeerGetConfigurationMessage *msg;
  struct GNUNET_TESTBED_Controller *c;
  uint64_t op_id;

  GNUNET_assert (NULL != data);
  GNUNET_assert (NULL != data->peer);
  c = data->peer->controller;
  op_id = GNUNET_TESTBED_get_next_op_id (c);
  msg =
      GNUNET_TESTBED_generate_peergetconfig_msg_ (data->peer->unique_id, op_id);
  data->opc =
      GNUNET_TESTBED_forward_operation_msg_ (c, op_id, &msg->header,
                                             &configuration_receiver, data);
  GNUNET_free (msg);
  data->state = CFG_REQUEST_QUEUED;
}
/**
 * Create the given peer at the specified host using the given
 * controller.  If the given controller is not running on the target
 * host, it should find or create a controller at the target host and
 * delegate creating the peer.  Explicit delegation paths can be setup
 * using 'GNUNET_TESTBED_controller_link'.  If no explicit delegation
 * path exists, a direct link with a subordinate controller is setup
 * for the first delegated peer to a particular host; the subordinate
 * controller is then destroyed once the last peer that was delegated
 * to the remote host is stopped.  This function is used in particular
 * if some other controller has already assigned a unique ID to the
 * peer.
 *
 * Creating the peer only creates the handle to manipulate and further
 * configure the peer; use "GNUNET_TESTBED_peer_start" and
 * "GNUNET_TESTBED_peer_stop" to actually start/stop the peer's
 * processes.
 *
 * Note that the given configuration will be adjusted by the
 * controller to avoid port/path conflicts with other peers.
 * The "final" configuration can be obtained using
 * 'GNUNET_TESTBED_peer_get_information'.
 *
 * @param unique_id unique ID for this peer
 * @param controller controller process to use
 * @param host host to run the peer on
 * @param cfg Template configuration to use for the peer. Should exist until
 *          operation is cancelled or GNUNET_TESTBED_operation_done() is called
 * @param cb the callback to call when the peer has been created
 * @param cls the closure to the above callback
 * @return the operation handle
 */
struct GNUNET_TESTBED_Operation *
GNUNET_TESTBED_peer_create_with_id_ (uint32_t unique_id,
                                     struct GNUNET_TESTBED_Controller
                                     *controller,
                                     struct GNUNET_TESTBED_Host *host,
                                     const struct GNUNET_CONFIGURATION_Handle
                                     *cfg, GNUNET_TESTBED_PeerCreateCallback cb,
                                     void *cls)
{
  struct GNUNET_TESTBED_Peer *peer;
  struct PeerCreateData *data;
  struct OperationContext *opc;

  peer = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Peer));
  peer->controller = controller;
  peer->host = host;
  peer->unique_id = unique_id;
  peer->state = PS_INVALID;
  data = GNUNET_malloc (sizeof (struct PeerCreateData));
  data->host = host;
  data->cfg = cfg;
  data->cb = cb;
  data->cls = cls;
  data->peer = peer;
  opc = GNUNET_malloc (sizeof (struct OperationContext));
  opc->c = controller;
  opc->data = data;
  opc->id = GNUNET_TESTBED_get_next_op_id (controller);
  opc->type = OP_PEER_CREATE;
  opc->op =
      GNUNET_TESTBED_operation_create_ (opc, &opstart_peer_create,
                                        &oprelease_peer_create);
  GNUNET_TESTBED_operation_queue_insert_ (controller->opq_parallel_operations,
                                          opc->op);
  GNUNET_TESTBED_operation_begin_wait_ (opc->op);
  return opc->op;
}