#include <stdio.h>
#include <string.h>

#include "configuration.h"

#include "persistentItem.h"
#include "problema.h"
#include "method.h"
#include "result.h"

#include "logger.h"
#include "utilities.h"

#define PRESENTATION_TEMPLATE "\n/======================================================================/\n/ Result (id:%3d)\n/    - Problem (id:%3d)\n/    - Method  (id:%3d)\n/ Initial context :\n%s/ Result values :\n%s/======================================================================/\n"
#define ENTRY_TEMPLATE        "%s/    - %-22s : %s\n"

static
unsigned int getDatabaseResultId(Result * r) {
	return r->_result_id->getId(r->_result_id);
}
static
void setDatabaseResultId(Result * r, unsigned int id) {
	if (!r->_result_id)
		r->_result_id = createPersistentItem(id);
	else
		r->_result_id->_db_id = id;
}
static
unsigned int getDatabaseProblemId(Result * r) {
	return r->_problem_id->getId(r->_problem_id);
}
static
void setDatabaseProblemId(Result * r, unsigned int id) {
	if (!r->_problem_id)
		r->_problem_id = createPersistentItem(id);
	else
		r->_problem_id->_db_id = id;
}
static
unsigned int getDatabaseMethodId(Result * r) {
	return r->_method_id->getId(r->_method_id);
}
static
void setDatabaseMethodId(Result * r, unsigned int id) {
	if (!r->_method_id)
		r->_method_id = createPersistentItem(id);
	else
		r->_method_id->_db_id = id;
}

static
char* displayResult(Result * r) {
	static char output[MAX_STRING_INPUT];
	memset(output, '\0', MAX_STRING_INPUT * sizeof(char));

	if (r->_data_loaded) {
		char initialContext[MAX_STRING_INPUT] = { 0 };
		char resultValues[MAX_STRING_INPUT] = { 0 };

		if (r->_data_loaded & INITIAL_VECTOR_LOADED)
			sprintf(initialContext, ENTRY_TEMPLATE, initialContext,
					"initial vector", printVector(r->_initial_vector,
							r->_problem_dimension));

		if (r->_initialParameters.data_loaded & PATIENCE_LOADED)
			sprintf(
					initialContext,
					ENTRY_TEMPLATE,
					initialContext,
					"patience",
					integerToString(
							*((int *) r->_initialParameters.parameters[INDEX_PATIENCE])));
		if (r->_initialParameters.data_loaded & EPSILON_LOADED)
			sprintf(
					initialContext,
					ENTRY_TEMPLATE,
					initialContext,
					"epsilon",
					doubleToString(
							*((double *) r->_initialParameters.parameters[INDEX_EPSILON])));
		if (r->_initialParameters.data_loaded & BOUNDS_MIN_LOADED)
			sprintf(
					initialContext,
					ENTRY_TEMPLATE,
					initialContext,
					"borne-inferieure",
					doubleToString(
							*((double *) r->_initialParameters.parameters[INDEX_BOUNDS_MIN])));
		if (r->_initialParameters.data_loaded & BOUNDS_MAX_LOADED)
			sprintf(
					initialContext,
					ENTRY_TEMPLATE,
					initialContext,
					"borne-superieure",
					doubleToString(
							*((double *) r->_initialParameters.parameters[INDEX_BOUNDS_MAX])));
		if (r->_initialParameters.data_loaded & STEP_LOADED)
			sprintf(
					initialContext,
					ENTRY_TEMPLATE,
					initialContext,
					"pas",
					doubleToString(
							*((double *) r->_initialParameters.parameters[INDEX_STEP])));
		if (r->_initialParameters.data_loaded & TEMPERATURE_LOADED)
			sprintf(
					initialContext,
					ENTRY_TEMPLATE,
					initialContext,
					"temperature",
					doubleToString(
							*((double *) r->_initialParameters.parameters[INDEX_TEMPERATURE])));
		if (r->_initialParameters.data_loaded & NEIGHBOR_STEP_LOADED)
			sprintf(
					initialContext,
					ENTRY_TEMPLATE,
					initialContext,
					"pas-exploration",
					doubleToString(
							*((double *) r->_initialParameters.parameters[INDEX_NEIGHBOR_STEP])));
		if (r->_initialParameters.data_loaded & LEVEL_LIMIT_LOADED)
			sprintf(
					initialContext,
					ENTRY_TEMPLATE,
					initialContext,
					"maximum-iteration-niveau",
					integerToString(
							*((int *) r->_initialParameters.parameters[INDEX_LEVEL_LIMIT])));
		if (r->_initialParameters.data_loaded & POPULATION_SIZE_LOADED)
			sprintf(
					initialContext,
					ENTRY_TEMPLATE,
					initialContext,
					"taille-population",
					integerToString(
							*((int *) r->_initialParameters.parameters[INDEX_POPULATION_SIZE])));
		if (r->_initialParameters.data_loaded & CROSS_PROBA_LOADED)
			sprintf(
					initialContext,
					ENTRY_TEMPLATE,
					initialContext,
					"probabilite-croisement",
					doubleToString(
							*((double *) r->_initialParameters.parameters[INDEX_CROSS_PROBA])));
		if (r->_initialParameters.data_loaded & MUTATION_PROBA_LOADED)
			sprintf(
					initialContext,
					ENTRY_TEMPLATE,
					initialContext,
					"probabilite-mutation",
					doubleToString(
							*((double *) r->_initialParameters.parameters[INDEX_MUTATION_PROBA])));

		if (r->_data_loaded & RESULT_VECTOR_LOADED)
			sprintf(resultValues, ENTRY_TEMPLATE, resultValues,
					"optimum vector", printVector(r->_result_vector,
							r->_problem_dimension));
		if (r->_data_loaded & RESULT_VALUE_LOADED)
			sprintf(resultValues, ENTRY_TEMPLATE, resultValues,
					"optimum value", doubleToString(r->_result_value));
		if (r->_data_loaded & RESULT_COMPLEXITY_LOADED)
			sprintf(resultValues, ENTRY_TEMPLATE, resultValues, "complexity",
					integerToString(r->_result_complexity));
		if (r->_data_loaded & RESULT_ERROR_LOADED)
			sprintf(resultValues, ENTRY_TEMPLATE, resultValues, "error",
					doubleToString(r->_result_error));

		sprintf(output, PRESENTATION_TEMPLATE, r->getId(r), r->getProblemId(r),
				r->getMethodId(r), initialContext, resultValues);
	} else {
		strcpy(output, "No data loaded");
	}
	return output;
}

Result* createResultSkeleton() {
	Result *entity = NULL;
	int i;

	entity = (Result *) malloc(sizeof(Result));

	entity->_result_id = NULL;
	entity->_method_id = NULL;
	entity->_problem_id = NULL;

	entity->_data_loaded = 0;
	entity->_initialParameters.data_loaded = 0;

	for (i = 0; i < MAX_ITEM; i++)
		entity->_initialParameters.parameters[i] = NULL;

	entity->_initial_vector = NULL;
	entity->_result_vector = NULL;
	entity->_result_complexity = -1;
	entity->_result_error = -1;

	// method
	entity->setId = setDatabaseResultId;
	entity->getId = getDatabaseResultId;
	entity->setProblemId = setDatabaseProblemId;
	entity->getProblemId = getDatabaseProblemId;
	entity->setMethodId = setDatabaseMethodId;
	entity->getMethodId = getDatabaseMethodId;
	entity->display = displayResult;

	return entity;
}

Result* createResult(unsigned long id, unsigned long id_pb, unsigned long id_m) {
	Result *entity = NULL;

	entity = createResultSkeleton();

	entity->_result_id = createPersistentItem(id);
	entity->_method_id = createPersistentItem(id_pb);
	entity->_problem_id = createPersistentItem(id_m);

	return entity;
}

void destroyResult(Result *result) {
	if (result) {
		int i;
		for (i = 0; i < MAX_ITEM; i++)
			free(result->_initialParameters.parameters[i]);
		free(result->_result_id);
		free(result->_method_id);
		free(result->_problem_id);
		free(result);
	}
}
