#include "HistoryModule.h"
#include "printError.h"
#include <stdio.h>

#define MAX_HIST_LENGTH 1000

//using namespace std;

void HistoryModule :: initHistoryModule() {
	if(MAX_HIST_LENGTH <= vHistory.max_size()) {
		historyLength = MAX_HIST_LENGTH;
	}
	else {
		historyLength = vHistory.max_size();
	}

	delimitationMode = DELIMITATE_MODE_SRWF;
}

HistoryModule :: HistoryModule() {
	set_name(NO_NAME);
	initHistoryModule();
}

HistoryModule :: HistoryModule(char* name) {
	initHistoryModule();
	set_name(name);
}

bool HistoryModule :: set_maxHistoryLength(unsigned int length) {
	if(length <= MAX_HIST_LENGTH && length <= vHistory.max_size()) {
		this->historyLength = historyLength;
		return true;
	}
	return false;
}

unsigned int HistoryModule :: get_maxHistoryLength() {
	return historyLength;
}

bool HistoryModule :: set_delimitationMode(int delimitationMode) {
	if(delimitationMode > DELIMITATE_MODE_LBOUND && delimitationMode < DELIMITATE_MODE_UBOUND) {
		this->delimitationMode = delimitationMode;
		return true;
	}
	return false;
}

int HistoryModule :: get_delimitationMode() {
	return delimitationMode;
}

/*
bool HistoryModule :: add_entry(float entryValue)
{
	if(vHistory.size() <= historyLength) {
		HistoryEntry* historyEntry = new HistoryEntry();
		if(historyEntry != NULL) {
			historyEntry->set_entryValue(entryValue);
			try {
				vHistory.push_back(historyEntry);
				return true;
			}	
			catch(bad_alloc& error) {
				printError("bad_alloc caught: ", error.what());
			}
		}
		else {
			printError("Couldn't create HistoryEntry!");
		}
	}
	else {
		switch(delimitationMode) {
			case DELIMITATE_MODE_SRWF: 
				 printError("History is already full!");
				 break;
			default : 
				 printError("History is already full! DelimitationMode isn't set!");
				 break;
		}
	}
	return false;
}
*/

unsigned int HistoryModule :: get_numberOfEntries() {
	return vHistory.size();
}



/*
void HistoryModule :: initialize_history(unsigned int history_length, unsigned int recording_frequency, unsigned int delimitation_mode, int* success_indicator) {	
	history_occupied = 0;
	history_pointer = 0;

	clk_counter = 1;

	if(!set_history_length(history_length)) {
		*success_indicator = 1;
	}
	else if(!set_recording_frequency(recording_frequency)) {
		*success_indicator = 2;
	}
	else if(!set_delimitation_mode(delimitation_mode)) {
		*success_indicator = 3;
	}
	else {
		*success_indicator = 0;
	}
}

HistoryModule :: HistoryModule(unsigned int history_length, unsigned int recording_frequency, unsigned int delimitation_mode, int* success_indicator) {
	set_name(NO_NAME);
	initialize_history(history_length, recording_frequency, delimitation_mode, success_indicator);
}

HistoryModule :: HistoryModule(char* name, unsigned int history_length, unsigned int recording_frequency, unsigned int delimitation_mode, int* success_indicator) {

	set_name(name);
	initialize_history(history_length, recording_frequency, delimitation_mode, success_indicator);
}

bool HistoryModule :: set_history_length(unsigned int history_length) {
	if(history_length > 0 && history_length <= MAX_HIST_LENGTH) {
		this->history_length = history_length;
		return true;
	}
	this->history_length = 0;
	return false;
}

bool HistoryModule :: set_recording_frequency(unsigned int recording_frequency) {
	if(recording_frequency <= MAX_REC_FREQU) {
		this->recording_frequency = recording_frequency;
		return true;
	}
	this->recording_frequency = 0;
	return false;
}

bool HistoryModule :: set_delimitation_mode(unsigned int delimitation_mode) {
	if(delimitation_mode > DELIMITATE_MODE_LBOUND && delimitation_mode < DELIMITATE_MODE_UBOUND) {
		this->delimitation_mode = delimitation_mode;
		return true;
	}
	this->delimitation_mode = HIST_DELIMITATE_MODE_SRWF;
	return false;
}


void HistoryModule :: carry_on_history_pointer() {
	if(history_pointer >= history_length-1) {
		history_pointer = 0;
	}
	else {
		history_pointer++;
	}
}

//TODO: namen überarbeiten
bool HistoryModule :: drop_data(float data) {

	if(clk_counter == recording_frequency) {
		clk_counter = 1;
		return rec_data(data);
	}
	else {
		clk_counter++;
		return true;
	}

	return false;
}

bool HistoryModule :: rec_data(float data) {

	if(history_occupied < history_length-1) {
		
		history[history_pointer] = data;
		
		carry_on_history_pointer();
		history_occupied++;

		return true;
	} 
	else {

		if(delimitation_mode == HIST_DELIMITATE_MODE_SRWF) {
			return false;
		}
		else if(delimitation_mode == HIST_DELIMITATE_MODE_FIFO) {
			history[history_pointer] = data;
			carry_on_history_pointer();
			return true;
		}
		else if(delimitation_mode == HIST_DELIMITATE_MODE_LIFO) {
			//TODO: implement!
		}
		else {
			return false;
		}
	}

	return false;
}


bool HistoryModule :: get_latest_value(float* value) {
	if(history_occupied > 0) {
		if(history_pointer > 0) {
			*value = history[history_pointer-1];
		}
		else {
			*value = history[history_length-1];
		}
		return true;
	}

	return false;
}
















bool HistoryModule :: smooth_history() {
	if(history_occupied > 1) {

		unsigned int calculation_point;

		//TODO: kann man anders verschachteln .. in ein if mit || oder && 
		if(history_occupied < MAX_HIST_LENGTH-1) {
			calculation_point = 0;
		}
		else {
			if(history_pointer < MAX_HIST_LENGTH-1) {
				calculation_point = history_pointer;
			}
			else {
				calculation_point = 0;
			}
		}

		//printf("calculation_point = %u\n", calculation_point);

		for(unsigned int t_ix=0; t_ix<=history_occupied-1; t_ix++) {		
			
			float value, divider;
			
			if(calculation_point == 0) {
				//printf("hier?\n");
				if(history_occupied == MAX_HIST_LENGTH-1) {
					//printf("ja!\n");
					value = history[MAX_HIST_LENGTH-1];
					divider = 1;
				}
				else {
					value = 0;
					divider = 0;
				}

				value = value + 2*history[calculation_point];
				divider = divider + 2;

				//printf("value = %f\ndivider = %f\n", value, divider);


				//printf("is er da drinnen?\n");
				if(t_ix < history_occupied-1) {
					//printf("ja\n");
					value = value + history[calculation_point+1];
					divider = divider + 1;
				}
			}
			
			else if(calculation_point == MAX_HIST_LENGTH-1) {
				value = history[calculation_point-1] + 2*history[calculation_point];
				divider = 3;

				if(t_ix < history_occupied-1) {
					value = value + history[0];
					divider = divider + 1;
				}
			}
			
			else {
				value = history[calculation_point-1] + 2*history[calculation_point];
				divider = 3;

				//printf("da hoffentlich auch nicht\n");
				if(t_ix < history_occupied-1) {
					//printf("doch\n");
					value = value + history[calculation_point+1];
					divider = divider + 1;
				}
			}

			//printf("value = %f\ndivider = %f\ncalculation_point = %u\n", value, divider, calculation_point);
			history_smoothed[t_ix] = value/divider;

			//printf("history_smoothed[%u] = %f\n", t_ix, history_smoothed[t_ix]);


			if(calculation_point < MAX_HIST_LENGTH-1) {
				calculation_point++;
			}
			else {
				calculation_point = 0;
			}
		}
		return true;
	}
	return false;
}

//TODO: TO CHECK: small bug when overflow of history array ???
bool HistoryModule :: get_history_trend(float* trend, unsigned int time_unites, unsigned int time_base) {
	/*
	float trend_temp, divider;
	
	bool got_smoothed = smooth_history();
	
	if(got_smoothed) {
		if(time_unites <= MAX_HIST_LENGTH) {
			//TODO: fall wenn andere time base
			if(this->time_base == time_base) {

				if(time_unites < history_occupied) {   //XXX <= oder < ???
					//printf("a\n");
					trend_temp = history_smoothed[history_occupied-1] - history_smoothed[history_occupied-time_unites];	//xxx -1-time_unites oder nur -time_unites ???
					divider = (float)time_unites;
				}
				else {
					//printf("b\nhistory_smoothed[history_occupied-1] = %f mit history_occupied = %u\nhistory_smoothed[0] = %f\n", history_smoothed[history_occupied-1], history_occupied, history_smoothed[0]);
					trend_temp = history_smoothed[history_occupied-1] - history_smoothed[0];
					divider = (float)history_occupied;
					//printf("divider = %f\n", divider);
				}

				*trend = trend_temp/divider;
				//printf("trend = %f\n", *trend);
			}
			return true;
		}
	}
	*/
/*	return false;
}

bool HistoryModule :: get_history_trend_absolutely(float* trend, unsigned int time_unites, unsigned int time_base) {
	float trend_temp;
	bool got_trend = get_history_trend(&trend_temp, time_unites, time_base);

	if(got_trend) {
		if(trend_temp < 0) {
			trend_temp = trend_temp * -1;
		}
		*trend = trend_temp;

		return true;
	}

	return false;	
}

unsigned int HistoryModule :: get_history_occupied() {
	return history_occupied;
}

//TODO: Fehlerbehandlung!
float HistoryModule :: get_value_ago(unsigned int ago) {

	if(history_occupied >= ago) {
		if(history_pointer < ago) {
			return history[MAX_HIST_LENGTH-(ago-history_pointer)];	//TO CHECK .. auch ein -1 dazu??
		}
		else {
			return history[history_pointer-ago];
		}

	} else if (history_occupied > 0) {
		return history[0];
	}
	else {
		printf("\n\n\n\n\n\n\nBOESE");
		return 0;
	}
}






/*
void History :: set_history(unsigned int point, float value) {

	if(point<history_max_length) {
		if(next_history_pointer < history_max_length) {
			history[next_history_pointer] = value;
			next_history_pointer++;
		}
		else {
			//if(flag_ring_memory)	//TODO: Option(en) einbauen
			history[0] = value;
			next_history_pointer = 1;
		}
	}
	else {
		printf("Field %i of history array is out of range!\n", point);
	}
}
unsigned int History :: get_history(unsigned int point) {
	if(point<history_max_length) {
		return history[point];
	}
	else {
		printf("Field %i of history array is out of range!\n", point);
		return NULL;
	}
}

void History :: set_history_max_length(unsigned int max_length) {
	history_max_length = max_length;
}
unsigned int History :: get_history_max_length() {
	return history_max_length;
}
*/