/**
 * Release the connection to the neighbour.  The actual connection will be
 * closed if connections to other neighbour are waiting (to maintain a bound on
 * the total number of connections that are open).
 *
 * @param n the neighbour whose connection can be closed
 */
void
GST_neighbour_release_connection (struct Neighbour *n)
{
  GNUNET_assert (0 == n->inactive);
  GNUNET_assert (0 < n->reference_cnt);
  n->reference_cnt--;
  if (0 == n->reference_cnt)
  {
    n->inactive = 1;
    GNUNET_TESTBED_operation_inactivate_ (n->conn_op);
  }
}
/**
 * Function to cancel an operation (release all associated resources).  This can
 * be because of a call to "GNUNET_TESTBED_operation_cancel" (before the
 * operation generated an event) or AFTER the operation generated an event due
 * to a call to "GNUNET_TESTBED_operation_done".  Thus it is not guaranteed that
 * a callback to the 'OperationStart' preceeds the call to 'OperationRelease'.
 * Implementations of this function are expected to clean up whatever state is
 * in 'cls' and release all resources associated with the operation.
 */
static void
release_cb (void *cls)
{
  switch (result)
  {
  case TEST_OP1_STARTED:
    GNUNET_assert (&op1 == cls);
    result = TEST_OP1_RELEASED;
    op1 = NULL;
    step_task =
        GNUNET_SCHEDULER_add_delayed (STEP_DELAY, &step, NULL);
    break;
  case TEST_OP2_STARTED:
    GNUNET_assert (&op2 == cls);
    result = TEST_OP2_RELEASED;
    GNUNET_assert (NULL == step_task);
    break;
  case TEST_OP3_STARTED:
    GNUNET_assert (&op3 == cls);
    result = TEST_OP3_RELEASED;
    GNUNET_assert (NULL == step_task);
    break;
  case TEST_OP4_STARTED:
    GNUNET_assert (&op4 == cls);
    result = TEST_OP4_RELEASED;
    GNUNET_assert (NULL == step_task);
    op5 = GNUNET_TESTBED_operation_create_ (&op5, &start_cb, &release_cb);
    GNUNET_TESTBED_operation_queue_insert2_ (q1, op5, 1);
    GNUNET_TESTBED_operation_begin_wait_ (op5);
    op6 = GNUNET_TESTBED_operation_create_ (&op6, &start_cb, &release_cb);
    GNUNET_TESTBED_operation_queue_insert2_ (q2, op6, 1);
    GNUNET_TESTBED_operation_begin_wait_ (op6);
    op7 = GNUNET_TESTBED_operation_create_ (&op7, &start_cb, &release_cb);
    GNUNET_TESTBED_operation_queue_insert2_ (q1, op7, 1);
    GNUNET_TESTBED_operation_queue_insert2_ (q2, op7, 1);
    GNUNET_TESTBED_operation_begin_wait_ (op7);
    break;
  case TEST_OP5_6_7_STARTED:
    result = TEST_OP5_RELEASED;
    op5 = NULL;
    GNUNET_TESTBED_operation_release_ (op6);
    break;
  case TEST_OP5_RELEASED:
    op6 = NULL;
    result = TEST_OP6_RELEASED;
    GNUNET_TESTBED_operation_inactivate_ (op7);
    step_task = GNUNET_SCHEDULER_add_now (&step, NULL);
    break;
  case TEST_OP8_WAITING:
    GNUNET_assert (&op7 == cls);
    op7 = NULL;
    result = TEST_OP7_RELEASED;
    break;
  case TEST_OP8_ACTIVE:
    result = TEST_OP8_RELEASED;
    op8 = NULL;
    break;
  case TEST_OP9_STARTED:
    GNUNET_assert (&op9 == cls);
    result = TEST_OP9_RELEASED;
    GNUNET_TESTBED_operation_queue_destroy_ (q1);
    GNUNET_TESTBED_operation_queue_destroy_ (q2);
    q1 = NULL;
    q2 = NULL;
    break;
  default:
    GNUNET_assert (0);
  }
}
/**
 * Task to simulate artificial delay and change the test stage
 *
 * @param cls NULL
 */
static void
step (void *cls)
{
  GNUNET_assert (NULL != step_task);
  step_task = NULL;
  switch (result)
  {
  case TEST_OP1_STARTED:
    GNUNET_TESTBED_operation_release_ (op1);
    GNUNET_TESTBED_operation_queue_reset_max_active_ (q1, 0);
    op3 = GNUNET_TESTBED_operation_create_ (&op3, &start_cb, &release_cb);
    GNUNET_TESTBED_operation_queue_insert2_ (q1, op3, 2);
    GNUNET_TESTBED_operation_queue_insert2_ (q2, op3, 2);
    GNUNET_TESTBED_operation_begin_wait_ (op3);
    op4 = GNUNET_TESTBED_operation_create_ (&op4, &start_cb, &release_cb);
    GNUNET_TESTBED_operation_queue_insert2_ (q1, op4, 2);
    GNUNET_TESTBED_operation_queue_insert2_ (q2, op4, 2);
    GNUNET_TESTBED_operation_begin_wait_ (op4);
    break;
  case TEST_OP1_RELEASED:
    result = TEST_PAUSE;
    GNUNET_TESTBED_operation_queue_reset_max_active_ (q1, 2);
    break;
  case TEST_OP2_STARTED:
    GNUNET_TESTBED_operation_release_ (op2);
    break;
  case TEST_OP3_STARTED:
    GNUNET_TESTBED_operation_release_ (op3);
    break;
  case TEST_OP4_STARTED:
    GNUNET_TESTBED_operation_release_ (op4);
    break;
  case TEST_OP6_RELEASED:
    op8 = GNUNET_TESTBED_operation_create_ (&op8, &start_cb, &release_cb);
    GNUNET_TESTBED_operation_queue_insert2_ (q1, op8, 2);
    GNUNET_TESTBED_operation_queue_insert2_ (q2, op8, 2);
    result = TEST_OP8_WAITING;
    GNUNET_TESTBED_operation_begin_wait_ (op8);
    break;
  case TEST_OP8_STARTED:
    GNUNET_TESTBED_operation_inactivate_ (op8);
    result = TEST_OP8_INACTIVE_1;
    step_task = GNUNET_SCHEDULER_add_delayed (STEP_DELAY, &step, NULL);
    break;
  case TEST_OP8_INACTIVE_1:
    GNUNET_TESTBED_operation_activate_ (op8);
    result = TEST_OP8_ACTIVE;
    op9 = GNUNET_TESTBED_operation_create_ (&op9, &start_cb, &release_cb);
    GNUNET_TESTBED_operation_queue_insert2_ (q1, op9, 1);
    GNUNET_TESTBED_operation_queue_insert2_ (q2, op9, 1);
    GNUNET_TESTBED_operation_begin_wait_ (op9);
    step_task = GNUNET_SCHEDULER_add_delayed (STEP_DELAY, &step, NULL);
    break;
  case TEST_OP8_ACTIVE:
    GNUNET_TESTBED_operation_inactivate_ (op8);
    /* op8 should be released by now due to above call */
    GNUNET_assert (TEST_OP8_RELEASED == result);
    break;
  case TEST_OP9_STARTED:
    GNUNET_TESTBED_operation_release_ (op9);
    break;
  default:
    GNUNET_assert (0);
  }
}