/**
 * Callback for event from slave controllers
 *
 * @param cls NULL
 * @param event information about the event
 */
static void
slave_event_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
{
  struct LCFContext *lcf;

  /* We currently only get here when working on LCFContexts */
  GNUNET_assert (GNUNET_TESTBED_ET_OPERATION_FINISHED == event->type);
  lcf = event->op_cls;
  GNUNET_assert (lcf->op == event->op);
  GNUNET_TESTBED_operation_done (lcf->op);
  lcf->op = NULL;
  GNUNET_assert (FINISHED == lcf->state);
  GNUNET_assert (NULL != lcf->timeout_task);
  GNUNET_SCHEDULER_cancel (lcf->timeout_task);
  if (NULL == event->details.operation_finished.emsg)
    send_controller_link_response (lcf->client, lcf->operation_id,
                                   GNUNET_TESTBED_host_get_cfg_
                                   (GST_host_list[lcf->delegated_host_id]),
                                   NULL);
  else
    send_controller_link_response (lcf->client, lcf->operation_id,
                                   NULL,
                                   event->details.operation_finished.emsg);
  GNUNET_assert (NULL == lcf_proc_task_id);
  lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf);
  return;
}
/**
 * Callback to signal successfull startup of the controller process
 *
 * @param cls the handle to the slave whose status is to be found here
 * @param cfg the configuration with which the controller has been started;
 *          NULL if status is not #GNUNET_OK
 * @param status #GNUNET_OK if the startup is successfull; #GNUNET_SYSERR if not,
 *          GNUNET_TESTBED_controller_stop() shouldn't be called in this case
 */
static void
slave_status_cb (void *cls,
                 const struct GNUNET_CONFIGURATION_Handle *cfg,
                 int status)
{
  struct Slave *slave = cls;
  struct LinkControllersContext *lcc;

  lcc = slave->lcc;
  if (GNUNET_SYSERR == status)
  {
    slave->controller_proc = NULL;
    /* Stop all link controller forwarding tasks since we shutdown here anyway
       and as these tasks they depend on the operation queues which are created
       through GNUNET_TESTBED_controller_connect() and in kill_slave() we call
       the destructor function GNUNET_TESTBED_controller_disconnect() */
    GST_free_lcf ();
    kill_slave (slave);
    destroy_slave (slave);
    slave = NULL;
    LOG (GNUNET_ERROR_TYPE_WARNING, "Unexpected slave shutdown\n");
    GNUNET_SCHEDULER_shutdown ();       /* We too shutdown */
    goto clean_lcc;
  }
  slave->controller =
      GNUNET_TESTBED_controller_connect (GST_host_list[slave->host_id],
                                         EVENT_MASK, &slave_event_cb,
                                         slave);
  if (NULL != slave->controller)
  {
    send_controller_link_response (lcc->client, lcc->operation_id, cfg, NULL);
  }
  else
  {
    send_controller_link_response (lcc->client, lcc->operation_id, NULL,
                                   "Could not connect to delegated controller");
    kill_slave (slave);
    destroy_slave (slave);
    slave = NULL;
  }

 clean_lcc:
  if (NULL != lcc)
  {
    if (NULL != lcc->client)
    {
      GNUNET_SERVICE_client_continue (lcc->client);
      lcc->client = NULL;
    }
    GNUNET_free (lcc);
  }
  if (NULL != slave)
    slave->lcc = NULL;
}
/**
 * Task to be run upon timeout while attempting to connect to the neighbour
 *
 * @param cls the NeighbourConnectCtxt created in GST_handle_link_controllers()
 * @param tc the scheduler task context
 */
static void
timeout_neighbour_connect (void *cls,
                           const struct GNUNET_SCHEDULER_TaskContext *tc)
{
 struct NeighbourConnectCtxt *ncc = cls;

 ncc->timeout_task = NULL;
 send_controller_link_response (ncc->client, ncc->op_id, NULL,
                                "Could not connect to delegated controller");
 cleanup_ncc (ncc);
}
/**
 * Callback called when a connection to the neighbour is made
 *
 * @param cls the NeighbourConnectCtxt created in GST_handle_link_controllers()
 * @param c the handle the neighbour's controller
 */
static void
neighbour_connect_cb (void *cls, struct GNUNET_TESTBED_Controller *c)
{
  struct NeighbourConnectCtxt *ncc = cls;

  GNUNET_SCHEDULER_cancel (ncc->timeout_task);
  ncc->timeout_task = NULL;
  ncc->nh = NULL;
  GST_neighbour_release_connection (ncc->n);
  send_controller_link_response (ncc->client, ncc->op_id, NULL, NULL);
  cleanup_ncc (ncc);
}
/**
 * Task to free resources when forwarded link controllers has been timedout
 *
 * @param cls the LCFContext
 * @param tc the task context from scheduler
 */
static void
lcf_forwarded_operation_timeout (void *cls,
                                 const struct GNUNET_SCHEDULER_TaskContext *tc)
{
  struct LCFContext *lcf = cls;

  lcf->timeout_task = NULL;
  //  GST_forwarded_operation_timeout (lcf->fopc, tc);
  LOG (GNUNET_ERROR_TYPE_WARNING,
       "A forwarded controller link operation has timed out\n");
  send_controller_link_response (lcf->client, lcf->operation_id, NULL,
                                 "A forwarded controller link operation has "
                                 "timed out\n");
  GNUNET_assert (NULL == lcf_proc_task_id);
  lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf);
}