/*
 * EvaluationMatlab.h
 *
 *  Created on: Aug 5, 2015
 *      Author: jjpalacios
 */

#ifndef SRC_EVALUATIONMATLAB_H_
#define SRC_EVALUATIONMATLAB_H_

#define MATLAB_DEBUG 0

#include "./heading.h"
#include "./ecObjects/Evaluation.h"
#include "./utils/ParameterDefinition.h"
#include "engine.h" // MATLAB Engine API

namespace clbDEB {

// Maximum number of evaluations without restarting the connection
// to the Matlab engine
#define MATLAB_MAX_EVALS 2500

// Key words
#define MATLAB_INIT "_Init"
#define MATLAB_RUN "_Run"
#define MATLAB_DATA "_Data"
#define MATLAB_HISTORY "_History.csv"
#define MATLAB_BUFSIZE 1024
#define MATLAB_ERROR "fitness"
#define MATLAB_ERROR_MAE "mare"
#define MATLAB_ERROR_MSE "msre"

#define MATLAB_DATA_FOLDER "data-folder"	// Path to the folder containing the datasets
#define MATLAB_MODEL "model-name"	// Name of the model to use
#define MATLAB_DATASET "dataset-name"	// Name of the dataset to use
#define MATLAB_SIM_DAYS "sim-days"	// Number of days of simulation

#define MATLAB_NUM_OBJECTIVES "objectives" // Number of objectives to estimate
#define MATLAB_WEIGHTS "weights" // Weights for the objectives

// Set of global variables made by Matlab initializing script
#define MATLAB_GLOBAL_CHLA "data_chla" // Data for chlorophyll
#define MATLAB_GLOBAL_TEMP "data_temp" // Data for temperature
#define MATLAB_GLOBAL_LENGTH "initial_cm" // Data for initial length
#define MATLAB_GLOBAL_DAY "initial_day" // Data for the initial day
#define MATLAB_GLOBAL_WEIGHT "initial_gr" // Data for the initial weight
#define MATLAB_GLOBAL_SPAWN "data_spawn" // Data for the spawning days
#define MATLAB_GLOBAL_NDAYS "nDays" // Data for the number of days

// Threshold for the Yve parameter
#define YVE_LIMIT 0.8

enum Fitness_Error { MARE, MSRE };


//=============================================================================
//
//	Class EvaluationMatlab
//
//=============================================================================
/**
 * This class implements the evaluation function that is based on the Matlab
 * script provided by Ramón Filgueira.
 *
 * To use the DEB framework, this class must inherit from clbDEB::Evaluation
 * and be registered in the ClassRegister file.
 *
 * @author jjpalacios
 *
 */
class EvaluationMatlab : public Evaluation {
	//=========================================================================
	//		FIELDS
	//=========================================================================
public:
	/**
	 * Number of days for simulation
	 */
	int simulationDays;

	/**
	 * Type of fitness error
	 */
	short fitnessError;

	/**
	 * Number of objectives to estimate
	 */
	int numObjectives;

	/**
	 * Weights for the objectives
	 */
	std::vector<double> weights;


protected:
	/**
	 * Matlab engine. This is the basic structure to work with Matlab
	 */
	Engine* matlabEngine;

	/**
	 * Workspace to work with Matlab. It must contain the needed scripts for
	 * initialization
	 */
	std::string workspace;

	/**
	 * Model name to work with
	 */
	std::string model;

	/**
	 * Dataset to use for calibration
	 */
	std::vector< std::string > datasets;

	/**
	 * Historical data to make comparisons
	 */
	std::vector< std::vector< std::vector<double> > > history;

	/**
	 * Calibration scenario
	 */
	std::vector<ParameterDefinition *> parameters;

	/**
	 * MATLAB buffer. This contains the display data generated by MATLAB
	 */
	char matlabBuffer[MATLAB_BUFSIZE+1];

	/**
	 * Number of individuals evaluated in Matlab.
	 * Used to control when to free resources
	 */
	int matlabEvaluations;

	/**
	 * Output file for sensitivity analysis
	 */
	std::ofstream sensitivityFile;




	//=========================================================================
	//		CONSTRUCTORS / INITIALIZERS
	//=========================================================================
public:
	/**
	 * Default constructor
	 */
	explicit EvaluationMatlab(ParameterDB *parameters = NULL);

	/**
	 * Destructor
	 */
	virtual ~EvaluationMatlab() { };

	/**
	 * Loads the needed parameters: Fitness error
	 */
	virtual void setup(ParameterDB *parameters);

	/**
	 * Initializes the engine of Matlab
	 *
	 * @param workspace Path to the workspace in which Matlab has to work
	 */
	void init(ParameterDB *setup,
			std::vector<ParameterDefinition *> &params);

protected:
	/**
	 * Loads the data from the history files. These data will be
	 * used to evaluate the individuals
	 */
	void loadTargetData();


	/**
	 * Initializes the Matlab engine and loads all the scripts that are
	 * responsible for loading all the input data for each dataset
	 */
	void initMatlab();



	//=========================================================================
	//		METHODS
	//=========================================================================
public:
	/**
	 * Set the number of days to simulate
	 *
	 * @param simDays Number of days for simulations
	 */
	void setSimulationDays(const int simDays);

	/**
	 * Evaluates an entire population. This function is overridden so we can
	 * evaluate the entire population by making just one call to the MATLAB
	 * engine (thus saving time)
	 *
	 * @param population Population of individuals to evaluate
	 * @param svars Shared elements of the algorithm
	 * @param reEvaluate Force the evaluation of the individual
	 * @return the average fitness value
	 */
	virtual double evaluatePopulation(SharedVars *svars,
			Population *population, bool reEvaluate=false);

	/**
	 * Computed the fitness value for a given individual
	 *
	 * @param svars Shared elements of the algorithm*
	 * @param individual Individual to evaluate
	 * @return the fitness value
	 */
	virtual double evaluate(SharedVars *svars, Individual *individual);


	/**
	 * Get the name and setup of the operator
	 *
	 * @return A string of parameter values. The first string is the name of
	 * the operator
	 */
	virtual std::vector<std::string> getName() const {
		std::string longValue;
		std::vector<std::string> setup;
		setup.push_back("Matlab DEB");
		setup.push_back(";Model:;" + this->model);
		longValue = ";Datasets:;" + this->datasets[0];
		for(size_t i=1; i < datasets.size(); i++)
			longValue += ","+ this->datasets[i];
		setup.push_back(longValue);
		if(this->fitnessError == MARE)
			setup.push_back(";Fitness:;MARE");
		else if(this->fitnessError == MSRE)
			setup.push_back(";Fitness:;MSRE");
		setup.push_back(";Objectives:;" + valueToString(this->numObjectives));
		longValue = ";Weights:;" + valueToString(this->weights[0]);
		for(size_t i=1; i < weights.size(); i++)
			longValue += ":" + valueToString(weights[i]);
		setup.push_back(longValue);
		return setup;
	}


protected:
	/**
	 * Reads the weights for each objective from a formatted string
	 *
	 * @param textArray Formatted string with the weights separated by ';'
	 */
	void setWeights(std::string &textArray);


	/**
	 * Computes the fitness value of several individuals from the results
	 * given by the MATLAB model
	 *
	 * @param numIndividuals Number of individuals evaluated
	 * @param matlabResults Result given by the MATLAB model
	 * @return the fitness computed from that result
	 */
	std::vector<double> computeFitness(int dataset,
			int numIndividuals, mxArray *matlabResult) const;


	/**
	 * Checks if the setup is realistic and the Yve parameter is always
	 * lesser than 1. If not, returns the difference between Yve and 1
	 *
	 * @param numIndividuals Number of individuals evaluated
	 * @param matlabResults Result given by the MATLAB model
	 * @return an array of double values indicating how much is exceeded the
	 * value Yve=1
	 */
	std::vector<double> checkYve(int dataset,
			int numIndividuals, mxArray *matlabResult) const;


	/**
	 * Aggregates the fitness values of all datasets in one unique value by
	 * using the co-variation method.
	 *
	 * @param numIndividuals Number of individuals evaluated
	 * @param matlabResults Result given by the MATLAB model
	 * @return an array of double values indicating how much is exceeded the
	 * value Yve=1
	 */
	double aggregateFitness(std::vector<double> fitnessValues) const;


	/**
	 * Prints a Matlab command in the console
	 *
	 * @param command Matlab command to be printed
	 */
	void printMatlab(std::string &command);
};


}
#endif /* SRC_EVALUATIONMATLAB_H_ */
