#include<math.h>
#include<iostream>
#include"models.h"
#include"methods.h"
#include"SimulatedAnnealingParameters.h"

/*
 *	Optimization Methods
 */

/*
 *	Simulated Annealing
 */

//Constructor & Destructor

SimulatedAnnealing::SimulatedAnnealing(){
	Model* ToAdjust;
	double ampl = 0.1;
	double temp = 100.;
	double t_end = 1.;
	double cooling = 0.9;
	int length = 10;
	int max_f = 10;
	int current_f = 0;
}

SimulatedAnnealing::SimulatedAnnealing(Model* A){
	ToAdjust = A;
	double ampl = 0.1;
	double temp = 100.;
	double t_end = 1.;
	double cooling = 0.9;
	int length = 10;
	int max_f = 10;
	int current_f = 0;
}

SimulatedAnnealing::SimulatedAnnealing(Model* A, double a, double t, double t_e, double c, int l, int m): ampl(a),temp(t),t_end(t_e),cooling(c),length(l),max_f(m),current_f(0){
	ToAdjust = A;
}

//Initialisation, execution and finalisation

bool freeze(double T,double t_fin,int compt,int max){
	if(T>t_fin)
		return false;
	else{
		if(compt<max)
			return false;
		else
			return true;
	}
}

void dynamicdisplay(std::vector<double> par){
	FILE * fich = fopen("Opti.dynamic.data", "a");
	for(int i=0;i<par.size();i++)
		fprintf(fich, "%lf ", par[i]);
	fprintf(fich,"\n");
	fclose (fich);
}

#define PBSTR "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"
#define PBWIDTH 60

void printProgress(double percentage){
    int val = (int) (percentage * 100);
    int lpad = (int) (percentage * PBWIDTH);
    int rpad = PBWIDTH - lpad;
    printf ("\r%3d%% [%.*s%*s]", val, lpad, PBSTR, rpad, "");
    fflush (stdout);
}

void SimulatedAnnealing::initialise(){
	ampl = SA_Amplitude;
	temp = SA_TemperatureInitiale;
	t_end = SA_TemperatureFinale;
	cooling = SA_TauxDeRefroidissement;
	length = SA_NbIterationIsoT;
	max_f = SA_MaxRejet;
	current_f = 0;
}

void SimulatedAnnealing::execute(){
	double t_max = temp;
	printf ("System cooling :\n");
	while(not(freeze(temp,t_end,current_f,max_f))){
		printProgress(1.-(temp-t_end)/t_max);
		for(int i=0;i<length;i++){
			std::vector<double> param(ToAdjust->getParam());
			std::vector<double> n_param(ToAdjust->getNeighbor(ampl));
			double cost = ToAdjust->getCost();
			ToAdjust->setParam(n_param);
			double n_cost = ToAdjust->getCost();
			double diff = n_cost - cost;
			if(diff > 0){
				current_f++;
				double t = rand()/(RAND_MAX+1.), proba = exp(-diff/temp);
				if(t>proba){
					ToAdjust->setParam(param);
				}
			}
			else{
				//dynamicdisplay(ToAdjust->getParam());
				current_f = 0;
			}
		}
		double tmp = temp;
		temp = cooling*tmp;
	}
	printf ("\n");
}

void SimulatedAnnealing::finalise() const{
	//ToAdjust->exportModel();
	ToAdjust->displayModel();
}
