/**
 * @file tree-topology_migration.c
 *
 * @brief Tree-topology migration algorithm.
 *
 * This is an implementation of the service placement algorithm proposed in:
 * Konstantinos Oikonomou and Ioannis Stavrakakis. Scalable Service Migration:
 * The Tree Topology Case. In Proceedings of the Fifth Annual Mediterranean Ad
 * Hoc Networking Workshop (Med-Hoc-Net '06), Lipari, Italy, June 2006.
 *
 * It is applicable to a single service with only one service instance. This
 * service is iteratively migrated to a neighboring node over which more than
 * half of the service requests are forwarded.
 *
 * In our implementation, we estimate aggregate service demand by looking at
 * the past service traffic forwarded by direct (one-hop) neighbors of the
 * node that currently hosts the service (see Section 6 of the aforementioned
 * paper). We omit implementing the concept of a service monitoring entity (SME)
 * for simplicity.
 *
 * To avoid wrong migration decisions due to a small data set, we further
 * require a minimum number of service requests to be processed before running
 * the algorithm.
 *
 * Copyright 2007-2008, Freie Universitaet Berlin. All rights reserved.
 * Please refer to the COPYING file for detailed licensing information.
 *
 * @author Georg Wittenburg
 *
 * $Id: tree-topology_migration.c 2685 2008-06-25 16:38:56Z wittenbu $
 */

#include "../../types.h"
#include "../../vars.h"
#include "../../interface.h"
#include "../../interface_internal.h"
#include "../../util/debug.h"
#include "../../util/list.h"
#include "../../util/net.h"
#include "../../service/service.h"
#include "../../service/service_types.h"
#include "../placement.h"
#include "../placement_types.h"
#include "../placement_vars.h"
#include "../placement_types.h"
#include "tree-topology_migration.h"

/**
 * @brief Minimal number of data points to collect before running the algorithm.
 */
#define PLACEMENT_MIN_DATA_POINTS	100

/**
 * @brief Service migration callback.
 *
 * @param parameter Migration result.
 */
static void tree_topology_migration_callback(callback_parameter_t parameter) {
	SERVICE_migration_result_t* result = (SERVICE_migration_result_t*) parameter;

	switch(result->error_code) {
	case C_ERROR_OK:
		C_DEBUG(C_DEBUG_LEVEL_NOTE, "PLACEMENT: Migration of service \"%s\" was successful.\n", result->service_id);
		break;
	case C_ERROR_PARAMETER:
		C_DEBUG(C_DEBUG_LEVEL_ERROR, "PLACEMENT: Migration of service \"%s\" failed due to an invalid parameter.\n", result->service_id);
		break;
	case C_ERROR_TIMEOUT:
		C_DEBUG(C_DEBUG_LEVEL_ERROR, "PLACEMENT: Migration of service \"%s\" timed out.\n", result->service_id);
		break;
	case C_ERROR_BUSY:
		C_DEBUG(C_DEBUG_LEVEL_ERROR, "PLACEMENT: Migration of service \"%s\" failed because system is busy.\n", result->service_id);
		break;
	case C_ERROR_IN_PROGRESS:
		C_DEBUG(C_DEBUG_LEVEL_ERROR, "PLACEMENT: Migration of service \"%s\" failed due to an invalid parameter.\n", result->service_id);
		break;
	case C_ERROR_COMM_ERROR:
		C_DEBUG(C_DEBUG_LEVEL_ERROR, "PLACEMENT: Migration of service \"%s\" failed due to communication errors.\n", result->service_id);
		break;
	case C_ERROR_REMOTE_BUSY:
		C_DEBUG(C_DEBUG_LEVEL_ERROR, "PLACEMENT: Migration of service \"%s\" failed because the remote system refused.\n", result->service_id);
		break;
	default:
		C_DEBUG(C_DEBUG_LEVEL_FATAL, "PLACEMENT: Migration callback with unknown error code 0x%X.\n", result->error_code);
		abort();
	}
	FREE(result);
}

/**
 * @brief Tree-topology migration algorithm.
 *
 * @param service Pointer to service for which to run this algorithm.
 */
static void tree_topology_migration_run(SERVICE_service_t* service) {
	// Look for node that was the last hop to forward most of traffic to
	// this node.
	unsigned int max_last_hop_traffic = 0;
	node_t* node_with_max_last_hop_traffic = NULL;
	unsigned int total_traffic_via_other_last_hops;

	unsigned int i;
	for(i = 0; i < list_size(service->placement_data->node_stats); i++) {
		PLACEMENT_node_stats_t* node_stats = (PLACEMENT_node_stats_t*) list_get(service->placement_data->node_stats, i);
		if(node_stats->last_hop_traffic > max_last_hop_traffic) {
			max_last_hop_traffic = node_stats->last_hop_traffic;
			node_with_max_last_hop_traffic = &node_stats->node;
		}
	}

	// This basically can't happen but it can't hurt to check.
	if(node_with_max_last_hop_traffic == NULL) {
		C_DEBUG(C_DEBUG_LEVEL_FATAL, "PLACEMENT: %d service request(s) for service \"%s\" but no traffic.\n", service->placement_data->service_request_count, service->id);
		abort();
	}

	// Migrate to the last hop that forwarded most of the traffic if this
	// traffic is greater than the total traffic forwarded via the other
	// last-hop nodes.
	total_traffic_via_other_last_hops = service->placement_data->service_request_traffic - max_last_hop_traffic;
	if(
		(max_last_hop_traffic > total_traffic_via_other_last_hops)
		&& (node_with_max_last_hop_traffic->address != C_LOCAL_ADDR)
	) {
		SERVICE_migration_result_t* result = NULL;

		C_DEBUG(C_DEBUG_LEVEL_NOTE, "PLACEMENT: Migrating service \"%s\" to node %s (%d bytes after %d total requests, %d bytes from all other nodes)...\n", service->id, addr_to_str(node_with_max_last_hop_traffic->address), max_last_hop_traffic, service->placement_data->service_request_count, total_traffic_via_other_last_hops);

		MALLOC(result, SERVICE_migration_result_t);
		SERVICE_migrate_service(service->id, node_with_max_last_hop_traffic->address, tree_topology_migration_callback, result);
	} else if(node_with_max_last_hop_traffic->address == C_LOCAL_ADDR) {
		C_DEBUG(C_DEBUG_LEVEL_NOTE, "PLACEMENT: Service \"%s\" is placed optimally on the current node (%d bytes after %d total requests, %d bytes from all other nodes)...\n", service->id, addr_to_str(node_with_max_last_hop_traffic->address), max_last_hop_traffic, service->placement_data->service_request_count, total_traffic_via_other_last_hops);
	} else {
		C_DEBUG(C_DEBUG_LEVEL_INFO, "PLACEMENT: Migration of service \"%s\" is not necessary (%d bytes after %d total requests, %d bytes from all other nodes).\n", service->id, max_last_hop_traffic, service->placement_data->service_request_count, total_traffic_via_other_last_hops);
	}
}

/**
 * @brief Start tree-topology migration algorithm.
 *
 * @param service Pointer to service for which to start this algorithm.
 */
void tree_topology_migration_start(SERVICE_service_t* service) {
	// Nothing to do.
}

/**
 * @brief Stop tree-topology migration algorithm.
 *
 * @param service Pointer to service for which to stop this algorithm.
 */
void tree_topology_migration_stop(SERVICE_service_t* service) {
	// Nothing to do.
}

/**
 * @brief Trigger tree-topology migration algorithm.
 *
 * @param service Pointer to service for which to trigger this algorithm.
 */
void tree_topology_migration_trigger(SERVICE_service_t* service) {
	// Don't do anything if we don't have enough data points.
	if(service->placement_data->service_request_count < PLACEMENT_MIN_DATA_POINTS) {
		C_DEBUG(C_DEBUG_LEVEL_DEBUG, "PLACEMENT: Only %d of %d required data points in statistics of service \"%s\", not running algorithm...\n", service->placement_data->service_request_count, PLACEMENT_MIN_DATA_POINTS, service->id);
		return;
	}

	tree_topology_migration_run(service);
}
