/**
 * Signature of the event handler function called by the
 * respective event controller.
 *
 * @param cls closure
 * @param event information about the event
 */
static void
controller_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
{
  switch (result)
  {
  case SLAVE2_REGISTERED:
    check_operation_success (event);
    GNUNET_TESTBED_operation_done (op);
    op = NULL;
    result = SLAVE1_LINK_SUCCESS;
    GNUNET_assert (NULL != slave2);
    GNUNET_assert (NULL != slave);
    op = GNUNET_TESTBED_peer_create (mc, slave, cfg, peer_create_cb, NULL);
    GNUNET_assert (NULL != op);
    break;
  case SLAVE1_PEER_START_SUCCESS:
    check_operation_success (event);
    GNUNET_TESTBED_operation_done (op);
    result = SLAVE2_LINK_SUCCESS;
    op = GNUNET_TESTBED_peer_create (mc, slave2, cfg, peer_create_cb, NULL);    
    GNUNET_assert (NULL != op);
    break;
  case MASTER_PEER_CREATE_SUCCESS:
    GNUNET_assert (GNUNET_TESTBED_ET_PEER_START == event->type);
    GNUNET_assert (event->details.peer_start.host == host);
    GNUNET_assert (event->details.peer_start.peer == master_peer);
    GNUNET_TESTBED_operation_done (op);
    result = MASTER_PEER_START_SUCCESS;
    slave = GNUNET_TESTBED_host_create_with_id (1, "127.0.0.1", NULL, 0);
    GNUNET_assert (NULL != slave);
    rh = GNUNET_TESTBED_register_host (mc, slave, &registration_cont, NULL);
    GNUNET_assert (NULL != rh);
    break;
  case SLAVE1_PEER_CREATE_SUCCESS:
    GNUNET_assert (GNUNET_TESTBED_ET_PEER_START == event->type);
    GNUNET_assert (event->details.peer_start.host == slave);
    GNUNET_assert (event->details.peer_start.peer == slave1_peer);
    GNUNET_TESTBED_operation_done (op);
    result = SLAVE1_PEER_START_SUCCESS;
    op = GNUNET_TESTBED_controller_link (NULL, mc, slave2, slave, cfg,
                                         GNUNET_YES);  
    break;
  case SLAVE2_PEER_CREATE_SUCCESS:
    GNUNET_assert (GNUNET_TESTBED_ET_PEER_STOP == event->type);
    GNUNET_assert (event->details.peer_stop.peer == slave1_peer);
    GNUNET_TESTBED_operation_done (op);
    result = SLAVE1_PEER_STOP_SUCCESS;
    op = GNUNET_TESTBED_peer_start (NULL, slave2_peer, NULL, NULL);
    GNUNET_assert (NULL != op);
    break;
  case SLAVE1_PEER_STOP_SUCCESS:
    GNUNET_assert (GNUNET_TESTBED_ET_PEER_START == event->type);
    GNUNET_assert (event->details.peer_start.host == slave2);
    GNUNET_assert (event->details.peer_start.peer == slave2_peer);
    GNUNET_TESTBED_operation_done (op);
    result = SLAVE2_PEER_START_SUCCESS;
    op = GNUNET_TESTBED_overlay_connect (mc, NULL, NULL, master_peer,
                                         slave2_peer);
    break;
  case SLAVE2_PEER_START_SUCCESS:
    GNUNET_assert (NULL != event);
    GNUNET_assert (GNUNET_TESTBED_ET_CONNECT == event->type);
    GNUNET_assert (event->details.peer_connect.peer1 == master_peer);
    GNUNET_assert (event->details.peer_connect.peer2 == slave2_peer);
    result = MASTER_SLAVE2_PEERS_CONNECTED;
    GNUNET_TESTBED_operation_done (op);
    op = NULL;
    GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
                                  (GNUNET_TIME_UNIT_SECONDS, 1), &delay_task,
                                  NULL);
    break;
  case MASTER_SLAVE2_PEERS_CONNECTED:
    GNUNET_assert (GNUNET_TESTBED_ET_PEER_STOP == event->type);
    GNUNET_assert (event->details.peer_stop.peer == slave2_peer);
    GNUNET_TESTBED_operation_done (op);
    result = SLAVE2_PEER_STOP_SUCCESS;
    op = GNUNET_TESTBED_peer_destroy (slave1_peer);
    GNUNET_assert (NULL != op);
    break;
  case SLAVE2_PEER_STOP_SUCCESS:
    check_operation_success (event);
    GNUNET_TESTBED_operation_done (op);
    result = SLAVE1_PEER_DESTROY_SUCCESS;
    op = GNUNET_TESTBED_peer_destroy (slave2_peer);
    GNUNET_assert (NULL != op);
    break;
  case SLAVE1_PEER_DESTROY_SUCCESS:
    check_operation_success (event);
    GNUNET_TESTBED_operation_done (op);
    op = NULL;
    result = SLAVE2_PEER_DESTROY_SUCCESS;
    slave3 = GNUNET_TESTBED_host_create_with_id (3, "127.0.0.1", NULL, 0);
    rh = GNUNET_TESTBED_register_host (mc, slave3, &registration_cont, NULL);
    break;
  case SLAVE3_REGISTERED:
    check_operation_success (event);
    GNUNET_TESTBED_operation_done (op);
    op = NULL;
    result = SLAVE3_STARTED;
    op = GNUNET_TESTBED_get_slave_config (NULL, mc, slave3);
    GNUNET_assert (NULL != op);    
    break;
  case SLAVE3_STARTED:
    GNUNET_assert (NULL != event);
    GNUNET_assert (GNUNET_TESTBED_ET_OPERATION_FINISHED == event->type);
    GNUNET_assert (event->details.operation_finished.operation == op);
    GNUNET_assert (NULL == event->details.operation_finished.op_cls);
    GNUNET_assert (NULL == event->details.operation_finished.emsg);
    cfg3 = GNUNET_CONFIGURATION_dup (event->details.operation_finished.generic);
    GNUNET_TESTBED_operation_done (op);
    result = SLAVE3_GET_CONFIG_SUCCESS;
    op = GNUNET_TESTBED_controller_link (NULL, mc, slave3, slave, cfg3, GNUNET_NO);
    break;
  case SLAVE3_GET_CONFIG_SUCCESS:
    result = SLAVE3_LINK_SUCCESS;
    GNUNET_TESTBED_operation_done (op);
    op = GNUNET_TESTBED_peer_destroy (master_peer);
    break;
 case SLAVE3_LINK_SUCCESS:
   check_operation_success (event);
   result = SUCCESS;
   GNUNET_TESTBED_operation_done (op);
   op = NULL;       
   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
                                 (GNUNET_TIME_UNIT_SECONDS, 3),
                                 &do_shutdown, NULL);
   break;
  default:
    GNUNET_assert (0);
  }
}
/**
 * Signature of the event handler function called by the
 * respective event controller.
 *
 * @param cls closure
 * @param event information about the event
 */
static void
controller_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
{
    switch (event->type)
    {
    case GNUNET_TESTBED_ET_OPERATION_FINISHED:
        if ((NULL != event->op_cls) ||
                (NULL != event->details.operation_finished.emsg))
        {
            GNUNET_break (0);
            abort_test ();
            return;
        }
        switch (result)
        {
        case PEERS_STOPPED:
            if (NULL != event->details.operation_finished.generic)
            {
                GNUNET_break (0);
                abort_test ();
                return;
            }
            if (event->op == peer1.operation)
            {
                GNUNET_TESTBED_operation_done (peer1.operation);
                peer1.operation = NULL;
                peer1.peer = NULL;
            }
            else if (event->op == peer2.operation)
            {
                GNUNET_TESTBED_operation_done (peer2.operation);
                peer2.operation = NULL;
                peer2.peer = NULL;
            }
            else if (event->op == peer3.operation)
            {
                GNUNET_TESTBED_operation_done (peer3.operation);
                peer3.operation = NULL;
                peer3.peer = NULL;
            }
            else
            {
                GNUNET_break (0);
                abort_test ();
                return;
            }
            if ((NULL == peer1.peer) && (NULL == peer2.peer) && (NULL == peer3.peer))
            {
                result = SUCCESS;
                GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
            }
            break;
        case PEER1_STARTED:
            if ((NULL != event->details.operation_finished.generic) ||
                    (NULL == common_operation))
            {
                GNUNET_break (0);
                abort_test ();
                return;
            }
            GNUNET_TESTBED_operation_done (common_operation);
            common_operation = NULL;
            result = CONTROLLER2_UP;
            peer2.operation =
                GNUNET_TESTBED_peer_create (controller1, neighbour1, cfg,
                                            &peer_create_cb, NULL);
            if (NULL == peer2.operation)
            {
                GNUNET_break (0);
                abort_test ();
                return;
            }
            break;
        case PEER2_STARTED:
            if ((NULL != event->details.operation_finished.generic) ||
                    (NULL == common_operation))
            {
                GNUNET_break (0);
                abort_test ();
                return;
            }
            GNUNET_TESTBED_operation_done (common_operation);
            common_operation = NULL;
            result = CONTROLLER3_UP;
            peer3.operation =
                GNUNET_TESTBED_peer_create (controller1, neighbour2, cfg,
                                            &peer_create_cb, NULL);
            if (NULL == peer3.operation)
            {
                GNUNET_break (0);
                abort_test ();
                return;
            }
            break;
        default:
            GNUNET_break (0);
            abort_test ();
            return;
        }
        break;
    case GNUNET_TESTBED_ET_PEER_START:
        switch (result)
        {
        case PEER1_CREATED:
            if (event->details.peer_start.host != host)
            {
                GNUNET_break (0);
                abort_test ();
                return;
            }
            peer1.is_running = GNUNET_YES;
            GNUNET_TESTBED_operation_done (peer1.operation);
            peer1.operation = NULL;
            result = PEER1_STARTED;
            common_operation =
                GNUNET_TESTBED_controller_link (NULL, controller1, neighbour1, NULL,
                                                GNUNET_YES);
            break;
        case PEER2_CREATED:
            if (event->details.peer_start.host != neighbour1)
            {
                GNUNET_break (0);
                abort_test ();
                return;
            }
            peer2.is_running = GNUNET_YES;
            GNUNET_TESTBED_operation_done (peer2.operation);
            peer2.operation = NULL;
            result = PEER2_STARTED;
            if (NULL != common_operation)
            {
                GNUNET_break (0);
                abort_test ();
                return;
            }
            common_operation =
                GNUNET_TESTBED_controller_link (NULL, controller1, neighbour2, NULL,
                                                GNUNET_YES);
            if (NULL == common_operation)
            {
                GNUNET_break (0);
                abort_test ();
                return;
            }
            break;
        case PEER3_CREATED:
            if (event->details.peer_start.host != neighbour2)
            {
                GNUNET_break (0);
                abort_test ();
                return;
            }
            peer3.is_running = GNUNET_YES;
            GNUNET_TESTBED_operation_done (peer3.operation);
            peer3.operation = NULL;
            result = PEER3_STARTED;
            common_operation =
                GNUNET_TESTBED_overlay_connect (NULL, &op_comp_cb, NULL, peer2.peer,
                                                peer1.peer);
            break;
        default:
            GNUNET_break (0);
            abort_test ();
            return;
        }
        break;
    case GNUNET_TESTBED_ET_PEER_STOP:
        if (PEERS_CONNECTED_2 != result)
        {
            GNUNET_break (0);
            abort_test ();
            return;
        }
        if (event->details.peer_stop.peer == peer1.peer)
        {
            peer1.is_running = GNUNET_NO;
            GNUNET_TESTBED_operation_done (peer1.operation);
        }
        else if (event->details.peer_stop.peer == peer2.peer)
        {
            peer2.is_running = GNUNET_NO;
            GNUNET_TESTBED_operation_done (peer2.operation);
        }
        else if (event->details.peer_stop.peer == peer3.peer)
        {
            peer3.is_running = GNUNET_NO;
            GNUNET_TESTBED_operation_done (peer3.operation);
        }
        else
        {
            GNUNET_break (0);
            abort_test ();
            return;
        }
        if ((GNUNET_NO == peer1.is_running) && (GNUNET_NO == peer2.is_running) &&
                (GNUNET_NO == peer3.is_running))
        {
            result = PEERS_STOPPED;
            peer1.operation = GNUNET_TESTBED_peer_destroy (peer1.peer);
            peer2.operation = GNUNET_TESTBED_peer_destroy (peer2.peer);
            peer3.operation = GNUNET_TESTBED_peer_destroy (peer3.peer);
        }
        break;
    case GNUNET_TESTBED_ET_CONNECT:
        if ((NULL != peer1.operation) || (NULL != peer2.operation) ||
                (NULL != peer3.operation) || (NULL == common_operation))
        {
            GNUNET_break (0);
            abort_test ();
            return;
        }
        switch (result)
        {
        case PEER3_STARTED:
            if ((event->details.peer_connect.peer1 != peer2.peer) ||
                    (event->details.peer_connect.peer2 != peer1.peer))
            {
                GNUNET_break (0);
                abort_test ();
                return;
            }
            GNUNET_TESTBED_operation_done (common_operation);
            common_operation = NULL;
            result = PEERS_1_2_CONNECTED;
            LOG (GNUNET_ERROR_TYPE_DEBUG, "Peers connected\n");
            common_operation =
                GNUNET_TESTBED_overlay_connect (NULL, &op_comp_cb, NULL, peer2.peer,
                                                peer3.peer);
            break;
        case PEERS_1_2_CONNECTED:
            if ((event->details.peer_connect.peer1 != peer2.peer) ||
                    (event->details.peer_connect.peer2 != peer3.peer))
            {
                GNUNET_break (0);
                abort_test ();
                return;
            }
            GNUNET_TESTBED_operation_done (common_operation);
            common_operation = NULL;
            result = PEERS_2_3_CONNECTED;
            delayed_connect_task =
                GNUNET_SCHEDULER_add_delayed (TIME_REL_SECS (3), &do_delayed_connect,
                                              NULL);
            break;
        case PEERS_2_3_CONNECTED:
            if ((event->details.peer_connect.peer1 != peer1.peer) ||
                    (event->details.peer_connect.peer2 != peer2.peer))
            {
                GNUNET_break (0);
                abort_test ();
                return;
            }
            GNUNET_TESTBED_operation_done (common_operation);
            common_operation = NULL;
            result = PEERS_CONNECTED_2;
            LOG (GNUNET_ERROR_TYPE_DEBUG, "Peers connected again\n");
            peer1.operation = GNUNET_TESTBED_peer_stop (NULL, peer1.peer, NULL, NULL);
            peer2.operation = GNUNET_TESTBED_peer_stop (NULL, peer2.peer, NULL, NULL);
            peer3.operation = GNUNET_TESTBED_peer_stop (NULL, peer3.peer, NULL, NULL);
            break;
        default:
            GNUNET_break (0);
            abort_test ();
            return;
        }
        break;
    default:
        GNUNET_break (0);
        abort_test ();
        return;
    }
}