#include <unistd.h>
#include "ControlBandit.hpp"

/* --- Public --- */

ControlBandit::ControlBandit() {
	//Initialisation de la queue pour les 1er passes
	for(int i = 0; i < 2; i++) {
		//2 tours pour les 2 passe initiale
		for(int f = 2; f < 6; f++) {
			for(int h = 0; h < 7; h++) {
				fillAndHeuristic fah;
				fah.fill = f;
				fah.heuristic = h;
				queue.push_back(fah);
			}
		}
	}
	//Remplissage tableau moyenne
	for(int f = 0; f < NB_FILL; f++) {
		fema[f] = 0;
	}
	for(int h = 0; h < NB_HEURISTIC; h++){
		hema[h] = 0;
	}
	//Ini method
	alphaStatic = true;
	alpha = 0.5;
	method = MT_EMA;
	epsilon = 10;
	//Ini random
	srand(getpid());
}

ControlBandit::~ControlBandit() {

}

void ControlBandit::setMethod(int cstMethod, bool statique) {
	if(statique) {
		alphaStatic = true;
		alpha = ALPHA_STATIC;
	} else {
		alphaStatic = false;
		alpha = ALPHA_MIN;
	}
	method = cstMethod;
}

void ControlBandit::setEpsilon(double newEpsilon) {
	if(newEpsilon >= 0 && newEpsilon <= 1) {
		epsilon = (int) (newEpsilon * 100);
	}
}

bool ControlBandit::queueIsEmpty() {
	bool res;
	mutex.lock();
	res = queue.size() == 0;
	mutex.unlock();
	return res;
}

fillAndHeuristic ControlBandit::next() {
	fillAndHeuristic fah;
	mutex.lock();
	if(queue.size() == 0) {
		switch(method) {
			case MT_EPS:
				fah = this->methodEps();
				break;
			default:
				fah = this->methodEma();
		}
	} else {
		fah = queue[0];
		queue.erase(queue.begin());
	}
	mutex.unlock();
	return fah;
}

void ControlBandit::addFill(int num, int score) {
	num -= 2;
	//Recup la valeur d'alpha
	double localAlpha;
	if(!alphaStatic && alpha < ALPHA_MAX){
		//Increment la valeur
		mutex.lock();
		alpha += ALPHA_STEP;
		localAlpha = alpha;
		mutex.unlock();
	} else {
		localAlpha = alpha;
	}
	//Met a jour la moyenne
	mutexFill.lock();
	fema[num] = (1 - localAlpha) * fema[num] + localAlpha * score;
	mutexFill.unlock();
}

void ControlBandit::addHeuristic(int num, int score) {
	//Recup la valeur d'alpha
	double localAlpha;
	if(!alphaStatic && alpha < ALPHA_MAX){
		//Increment la valeur
		mutex.lock();
		alpha += ALPHA_STEP;
		localAlpha = alpha;
		mutex.unlock();
	} else {
		localAlpha = alpha;
	}
	mutexHeuristic.lock();
	hema[num] = (1 - localAlpha) * hema[num] + localAlpha * score;
	mutexHeuristic.unlock();
}

/* --- Private --- */

fillAndHeuristic ControlBandit::methodEma() {
	fillAndHeuristic fah;
	fah.fill = this->getBestFill();
	fah.heuristic = this->getBestHeuristic();
	return fah;
}

fillAndHeuristic ControlBandit::methodEps() {
	fillAndHeuristic fah;
	//Tirage au sort pour savoir si on prend la meilleur moyenne ou non
	int random = rand() % 100;
	if(random < epsilon) {
		//Tirage au sort de fill et heuristic
		fah.fill = this->getRandomFill();
		fah.heuristic = this->getRandomHeuristic();
		//printf("=== Random === : %d %d\n", fah.fill, fah.heuristic);
	} else {
		//Utilisation des meilleurs fill et heuristic
		fah.fill = this->getBestFill();
		fah.heuristic = this->getBestHeuristic();
	}
	return fah;
}

int ControlBandit::getBestFill() {
	int max = 0;
	int fill = 0;
	mutexFill.lock();
	for(int i = 0; i < NB_FILL; i++) {
		if(fema[i] > max) {
			max = fema[i];
			fill = i;
		}
	}
	mutexFill.unlock();
	return fill + 2;
}

int ControlBandit::getBestHeuristic() {
	int max = 0;
	int h = 0;
	mutexHeuristic.lock();
	for(int i = 0; i < NB_HEURISTIC; i++) {
		if(hema[i] > max) {
			max = hema[i];
			h = i;
		}
	}
	mutexHeuristic.unlock();
	return h;
}