/**
 * @file placement.c
 *
 * @brief Placement main file.
 *
 * Copyright 2007-2008, Freie Universitaet Berlin. All rights reserved.
 * Please refer to the COPYING file for detailed licensing information.
 *
 * @author Georg Wittenburg
 *
 * $Id: placement.c 2685 2008-06-25 16:38:56Z wittenbu $
 */

#include <string.h>

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



#define C_PLACEMENT_ALGORITHM_NONE			0x0	///< No placement algorithm.
#define C_PLACEMENT_ALGORITHM_SIMPLE_HEURISTIC		0x1	///< Simple heuristic algorithm.
#define C_PLACEMENT_ALGORITHM_TREE_TOPOLOGY_MIGRATION	0x2	///< Tree-topology migration placement algorihtm.
#define C_PLACEMENT_ALGORITHM_LINKPULL_MIGRATION	0x3	///< LinkPull migration algorithm.
#define C_PLACEMENT_ALGORITHM_PEERPULL_MIGRATION	0x4	///< PeerPull migration algorithm.



/*
 * Interface to main module.
 */

/**
 * @brief Initialize the placement module.
 */
void PLACEMENT_init() {
	C_PLACEMENT_active_algorithm = C_PLACEMENT_ALGORITHM_NONE;
}

/**
 * @brief Start the placement module.
 */
void PLACEMENT_start() {
}

/**
 * @brief Stop the placement module.
 */
void PLACEMENT_stop() {
}

/**
 * @brief Finalize the placement module.
 */
void PLACEMENT_finalize() {
}

/**
 * @brief Placement packet classifier.
 *
 * @param header Pointer to header of an incoming packet.
 * @param payload Pointer to payload of an incoming packet.
 */
void PLACEMENT_recv(const packet_header_t* header, const void* payload) {
	if(header->app != APP_TYPE_PLACEMENT) {
		C_DEBUG(C_DEBUG_LEVEL_FATAL, "PLACEMENT: Received non-PLACEMENT packet.\n");
		abort();
	}

	switch(header->type) {
	default:
		C_DEBUG(C_DEBUG_LEVEL_FATAL, "PLACEMENT: Received packet of unknown type 0x%X from node %s.\n", header->type, addr_to_str(header->from));
		abort();
	}
}



/*
 * Interface provided by this module.
 */

/**
 * @brief Allocate and initialize statistics for a service.
 *
 * @param service Pointer to service for which to allocate statistics.
 */
void PLACEMENT_alloc_stats(SERVICE_service_t* service) {
	if(service == NULL) {
		C_DEBUG(C_DEBUG_LEVEL_FATAL, "PLACEMENT: Trying to allocate statistics of NULL service.\n");
		abort();
	}
	alloc_stats(service);
}

/**
 * @brief Free statistics for a service.
 *
 * @param service Pointer to service whose statistics are to be freed.
 */
void PLACEMENT_free_stats(SERVICE_service_t* service) {
	if(service == NULL) {
		C_DEBUG(C_DEBUG_LEVEL_FATAL, "PLACEMENT: Trying to free statistics of NULL service.\n");
		abort();
	}
	free_stats(service);
}

/**
 * @brief Do statistics for a service request.
 *
 * @param service Pointer to service for which statistics are to be counted.
 * @param from Address of the node that originated the data request.
 * @param last_hop Address of the node that was the last to forward the data
 * request.
 * @param hops Number of times the data request was forwarded.
 * @param size Size of the data request in bytes.
 */
void PLACEMENT_stat_service_request(SERVICE_service_t* service, C_addr_t from, C_addr_t last_hop, uint8_t hops, size_t size) {
	if(service == NULL) {
		C_DEBUG(C_DEBUG_LEVEL_FATAL, "PLACEMENT: Trying to count data request on NULL service.\n");
		abort();
	}
	stat_service_request(service, from, last_hop, hops, size);
}

/**
 * @brief Start the selected service placement algorithm.
 *
 * This function is called once when the service is started.
 *
 * @param service Pointer to service for which the placement algorithm is to be
 * started.
 */
void PLACEMENT_start_algorithm(SERVICE_service_t* service) {
	if(service == NULL) {
		C_DEBUG(C_DEBUG_LEVEL_FATAL, "PLACEMENT: Trying to start placement algorithm for NULL service.\n");
		abort();
	}
	switch(C_PLACEMENT_active_algorithm) {
	case C_PLACEMENT_ALGORITHM_NONE:
		break;
	case C_PLACEMENT_ALGORITHM_SIMPLE_HEURISTIC:
		simple_heuristic_start(service);
		break;
	case C_PLACEMENT_ALGORITHM_TREE_TOPOLOGY_MIGRATION:
		tree_topology_migration_start(service);
		break;
	case C_PLACEMENT_ALGORITHM_LINKPULL_MIGRATION:
		linkpull_migration_start(service);
		break;
	case C_PLACEMENT_ALGORITHM_PEERPULL_MIGRATION:
		peerpull_migration_start(service);
		break;
	default:
		C_DEBUG(C_DEBUG_LEVEL_FATAL, "PLACEMENT: Invalid placement algorithm active (0x%X).\n", C_PLACEMENT_active_algorithm);
	}
}

/**
 * @brief Stop the selected service placement algorithm.
 *
 * This function is called once when the service is stopped.
 *
 * @param service Pointer to service for which the placement algorithm is to be
 * stopped.
 */
void PLACEMENT_stop_algorithm(SERVICE_service_t* service) {
	if(service == NULL) {
		C_DEBUG(C_DEBUG_LEVEL_FATAL, "PLACEMENT: Trying to stop placement algorithm for NULL service.\n");
		abort();
	}
	switch(C_PLACEMENT_active_algorithm) {
	case C_PLACEMENT_ALGORITHM_NONE:
		break;
	case C_PLACEMENT_ALGORITHM_SIMPLE_HEURISTIC:
		simple_heuristic_stop(service);
		break;
	case C_PLACEMENT_ALGORITHM_TREE_TOPOLOGY_MIGRATION:
		tree_topology_migration_stop(service);
		break;
	case C_PLACEMENT_ALGORITHM_LINKPULL_MIGRATION:
		linkpull_migration_stop(service);
		break;
	case C_PLACEMENT_ALGORITHM_PEERPULL_MIGRATION:
		peerpull_migration_stop(service);
		break;
	default:
		C_DEBUG(C_DEBUG_LEVEL_FATAL, "PLACEMENT: Invalid placement algorithm active (0x%X).\n", C_PLACEMENT_active_algorithm);
	}
}

/**
 * @brief Trigger the selected service placement algorithm.
 *
 * This function is called every time the service processes a request.
 *
 * @param service Pointer to service for which the placement algorithm is to be
 * triggered.
 */
void PLACEMENT_trigger_algorithm(SERVICE_service_t* service) {
	if(service == NULL) {
		C_DEBUG(C_DEBUG_LEVEL_FATAL, "PLACEMENT: Trying to trigger placement algorithm for NULL service.\n");
		abort();
	}
	switch(C_PLACEMENT_active_algorithm) {
	case C_PLACEMENT_ALGORITHM_NONE:
		break;
	case C_PLACEMENT_ALGORITHM_SIMPLE_HEURISTIC:
		simple_heuristic_trigger(service);
		break;
	case C_PLACEMENT_ALGORITHM_TREE_TOPOLOGY_MIGRATION:
		tree_topology_migration_trigger(service);
		break;
	case C_PLACEMENT_ALGORITHM_LINKPULL_MIGRATION:
		linkpull_migration_trigger(service);
		break;
	case C_PLACEMENT_ALGORITHM_PEERPULL_MIGRATION:
		peerpull_migration_trigger(service);
		break;
	default:
		C_DEBUG(C_DEBUG_LEVEL_FATAL, "PLACEMENT: Invalid placement algorithm active (0x%X).\n", C_PLACEMENT_active_algorithm);
	}
}



/*
 * Interface provided by this module for direct commands.
 */

/**
 * @brief Placement command function.
 *
 * @param argc Number of command parameters.
 * @param argv Array of strings used as command parameters.
 * @return <code>true</code> if the command was executed successfully,
 * <code>false</code> otherwise.
 */
bool PLACEMENT_command(int argc, const char*const* argv) {
	if(argc == 3) {
		if(strcmp(argv[1], "PLACEMENT_set_algorithm") == 0) {
			if(strcmp(argv[2], "none") == 0) {
				C_PLACEMENT_active_algorithm = C_PLACEMENT_ALGORITHM_NONE;
				return true;
			} else if(strcmp(argv[2], "simple_heuristic") == 0) {
				C_PLACEMENT_active_algorithm = C_PLACEMENT_ALGORITHM_SIMPLE_HEURISTIC;
				return true;
			} else if(strcmp(argv[2], "tree-topology_migration") == 0) {
				C_PLACEMENT_active_algorithm = C_PLACEMENT_ALGORITHM_TREE_TOPOLOGY_MIGRATION;
				return true;
			} else if(strcmp(argv[2], "linkpull_migration") == 0) {
				C_PLACEMENT_active_algorithm = C_PLACEMENT_ALGORITHM_LINKPULL_MIGRATION;
				return true;
			} else if(strcmp(argv[2], "peerpull_migration") == 0) {
				C_PLACEMENT_active_algorithm = C_PLACEMENT_ALGORITHM_PEERPULL_MIGRATION;
				return true;
			}
		}
	}
	return false;
}
