#include "rosa/agent/experimental/SubState.hpp"

//#include "printError.h"
#include "rosa/agent/experimental/relationChecker.hpp"

#include "rosa/agent/experimental/minmaxzeug.hpp"
#include <iostream>
#include <vector> 
#include <algorithm>  

SubState::SubState() {
	confidenceValidState = 0;
	confidenceInvalidState = 1;
}

void SubState::setSlot(SlaveAgentSlotOfAgent* slot) {
	this->slot = slot;
}

SlaveAgentSlotOfAgent* SubState::getSlot() {
	return slot;
}

bool SubState::addNewDiscreteAverage() {

	AverageValue* averageValue = new (nothrow) AverageValue();
	if (averageValue != NULL) {
		//try {
			vDiscreteAverage.push_back(averageValue);

			//printf("vDiscreteAverage size = %u\n", vDiscreteAverage.size());

			return true;
        /*}
		catch (bad_alloc& error) {
			printf("bad_alloc caught: %s", error.what());
			delete averageValue;
		}*/
	}
	return false;
}


bool SubState::injectValue() {
	float value;
	
	if (slot->get_slaveAgentValue(&value)) {
		statisticValue.injectAndCalculateStatisticValue(value);

		if (!vDiscreteAverage.empty()) {
			vDiscreteAverage.back()->injectAndCalculateAverageValue(value);
			return true;
		}
	}

	return false;
}


bool SubState::valueIsRelated(float thresholdToBeRelated) {
	float value;
	if (slot->get_slaveAgentValue(&value)) {
		return valueIsRelatedToReferenceValueOrBetweenMinAndMax(statisticValue.getAverageValue(), statisticValue.getMinimumValue(), statisticValue.getMaximumValue(), value, thresholdToBeRelated);
	}

	return false;
}

unsigned int SubState::getNumOfInjections() {
	return statisticValue.getInjectedValuesCounter();
}

bool SubState::lastDiscreteAverageBlockIsCompleted(unsigned int discreteAveragePartitionSize) {
	if (!vDiscreteAverage.empty()) {
		if (vDiscreteAverage.back()->getInjectedValuesCounter() < discreteAveragePartitionSize) {
			return false;
		}
	}
	return true;
}

unsigned int SubState::getNumberOfCompletedDiscreteAverageBlocks(unsigned int discreteAveragePartitionSize) {
	
	unsigned int numberOfDiscreteAverageBlocks = vDiscreteAverage.size();

	//printf("vDiscreteAverage.size() = %u\n", numberOfDiscreteAverageBlocks);

	if (!lastDiscreteAverageBlockIsCompleted(discreteAveragePartitionSize)) {
		numberOfDiscreteAverageBlocks--;
	}
	
	return vDiscreteAverage.size();
}


float SubState::getDiscreteAverageOfFirstBlock(unsigned int discreteAveragePartitionSize) {
	
	if (getNumberOfCompletedDiscreteAverageBlocks(discreteAveragePartitionSize) > 0) {
		return vDiscreteAverage.front()->getAverageValue();
	}
	//TODO: error handling - return 0 is not acceptable
	return 0;
}

float SubState::getDiscreteAverageOfLastBlock(unsigned int discreteAveragePartitionSize) {
	
	if (lastDiscreteAverageBlockIsCompleted(discreteAveragePartitionSize)) {
		return vDiscreteAverage.back()->getAverageValue();
	}
	else if (vDiscreteAverage.size() > 1) {
		return vDiscreteAverage.at(vDiscreteAverage.size()-1)->getAverageValue();
	}
	//TODO: error handling - return 0 is not acceptable
	return 0;
}

float SubState::getDiscreteAverageOfBlockBeforeLastBlock(unsigned int discreteAveragePartitionSize, unsigned int jumpBackDistance) {

	
		if (getNumberOfCompletedDiscreteAverageBlocks(discreteAveragePartitionSize) > jumpBackDistance) {

			if (lastDiscreteAverageBlockIsCompleted(discreteAveragePartitionSize)) {
				return vDiscreteAverage.at(vDiscreteAverage.size() - jumpBackDistance)->getAverageValue();
			}
			else {
				return vDiscreteAverage.at(vDiscreteAverage.size() - (jumpBackDistance + 1))->getAverageValue();
			}
			
		}
		else {
			return vDiscreteAverage.front()->getAverageValue();
		}
}

void SubState::deleteLastDiscreteAverageBlockIfNotCompleted(unsigned int discreteAveragePartitionSize) {
	if (!vDiscreteAverage.empty()) {
		if (vDiscreteAverage.back()->getInjectedValuesCounter() < discreteAveragePartitionSize) {
			vDiscreteAverage.pop_back();
		}
	}
}

//DATE18
float SubState::valueIsRelatedFuzzy(LinearFunctionBlock* SameState) {

	//XXX - Original war: valueIsRelatedToReferenceValueOrBetweenMinAndMax!
	float sampleValue;
	if (slot->get_slaveAgentValue(&sampleValue)) {
		printf("geht hinein - sample: %f, average: %f\n", sampleValue, statisticValue.getAverageValue());
		return SameState->getY(deviationValueReferenceValue(sampleValue, statisticValue.getAverageValue()));
	}

	printf("leider hier\n");

	//todo: isn't the best error handling
	return 0;
}

bool SubState::insertValueInSubState(LinearFunctionBlock* FuncBlockConfValStateDev, LinearFunctionBlock* FuncBlockConfInvStateDev, LinearFunctionBlock* FuncBlockConfValStateTime, LinearFunctionBlock* FuncBlockConfInvStateTime, unsigned int historySize) {

	bool insertionWorked = true;

	float sampleValue;
	if (slot->get_slaveAgentValue(&sampleValue)) {
		
		//statistic value
		statisticValue.injectAndCalculateStatisticValue(sampleValue);

		//DABs
		if (vDiscreteAverage.empty())
			insertionWorked = false;
		else
			vDiscreteAverage.back()->injectAndCalculateAverageValue(sampleValue);

		float worstConfidenceDeviation = 1;
		float bestConfidenceDeviation = 0;
		for (auto &historyValue : lSampleHistory) {
			bestConfidenceDeviation = maxValueOf2Values(bestConfidenceDeviation, FuncBlockConfInvStateDev->getY(deviationValueReferenceValue(sampleValue, historyValue)));
			worstConfidenceDeviation = minValueOf2Values(worstConfidenceDeviation, FuncBlockConfValStateDev->getY(deviationValueReferenceValue(sampleValue, historyValue)));
		}
		lBestConfidencesDeviation.push_front(bestConfidenceDeviation);
		lWorstConfidencesDeviation.push_front(worstConfidenceDeviation);

		//save actual value in history
		//try {
			lSampleHistory.push_front(sampleValue);
		/*}
		catch (bad_alloc& error) {
			printf("bad_alloc caught: %s", error.what());
			insertionWorked = false;
		}*/

		//delete last history- and deviation entry if history is full
		while (lSampleHistory.size() > historySize) {
			lSampleHistory.pop_back();
			lBestConfidencesDeviation.pop_back();
			lWorstConfidencesDeviation.pop_back();
		}

		
		//calculate the confidence with that the actual value fits to all of the history values
		bestConfidenceDeviation = 0;
		worstConfidenceDeviation = 1;
		for (auto &confDev : lBestConfidencesDeviation)
			bestConfidenceDeviation = minValueOf2Values(bestConfidenceDeviation, confDev);
		for (auto &confDev : lWorstConfidencesDeviation)
			worstConfidenceDeviation = maxValueOf2Values(worstConfidenceDeviation, confDev);


		//printf("confidence invalid time: %f\n", FuncBlockConfInvStateTime->getY((float)lSampleHistory.size()));


		confidenceValidState = minValueOf2Values(worstConfidenceDeviation, FuncBlockConfValStateTime->getY((float)lSampleHistory.size()));
		confidenceInvalidState = maxValueOf2Values(bestConfidenceDeviation, FuncBlockConfInvStateTime->getY((float)lSampleHistory.size()));
	}

	return insertionWorked;


	/*
	float value;
	
	if (slot->get_slaveAgentValue(&value)) {
		statisticValue.injectAndCalculateStatisticValue(value);

		if (!vDiscreteAverage.empty()) {
			vDiscreteAverage.back()->injectAndCalculateAverageValue(value);
			return true;
		}
	}

	return false;
	*/


}


float SubState::getConfidenceValidState() {
	return confidenceValidState;
}

float SubState::getConfidenceInvalidState() {
	return confidenceInvalidState;
}

float SubState::getConfVarIsSim2State(LinearFunctionBlock* FuncBlockConfSim2StateDev, LinearFunctionBlock* FuncBlockConfSim2StateTime) {
	
	float highestConfOf1Var = 0;
	float sampleValue;
	if (slot->get_slaveAgentValue(&sampleValue)) {

		vector<float> vDeviations;

		for (auto &h : lSampleHistory)
			vDeviations.push_back(deviationValueReferenceValue(sampleValue, h));

		sort(begin(vDeviations), end(vDeviations));

		//all adaptabilities within the history of one variable
		for (unsigned int numOfHistSamplesIncluded = 1; numOfHistSamplesIncluded <= vDeviations.size(); numOfHistSamplesIncluded++) {

			float lowestConfOfSamplesIncluded = 1;
			unsigned int histSampleCounter = 0;

			for (auto &deviation : vDeviations) {
				if (histSampleCounter >= numOfHistSamplesIncluded)
					break;
				lowestConfOfSamplesIncluded = fuzzyAND(lowestConfOfSamplesIncluded, FuncBlockConfSim2StateDev->getY(deviation));
				histSampleCounter++;
			}

			highestConfOf1Var = fuzzyOR(highestConfOf1Var, fuzzyAND(lowestConfOfSamplesIncluded, FuncBlockConfSim2StateTime->getY((float)histSampleCounter)));
		}
	}

	return highestConfOf1Var;
}

//Sorting with bigger Value in Front
struct descending
{
	template<class T>
	bool operator()(T const &a, T const &b) const { return a > b; }
};

float SubState::getConfVarIsDif2State(LinearFunctionBlock* FuncBlockConfDif2StateDev, LinearFunctionBlock* FuncBlockConfDif2StateTime) {
	
	//float highestConfOf1Var = 0;
	float highestConfOf1Var = 1;
	float sampleValue;
	if (slot->get_slaveAgentValue(&sampleValue)) {

		vector<float> vDeviations;

		for (auto &h : lSampleHistory)
			vDeviations.push_back(deviationValueReferenceValue(sampleValue, h));

		sort(begin(vDeviations), end(vDeviations), descending());

		//all adaptabilities within the history of one variable
		for (unsigned int numOfHistSamplesIncluded = 1; numOfHistSamplesIncluded <= vDeviations.size(); numOfHistSamplesIncluded++) {

			float highestConfOfSamplesIncluded = 0;
			unsigned int histSampleCounter = 0;

			for (auto &deviation : vDeviations) {
				if (histSampleCounter >= numOfHistSamplesIncluded)
					break;
				highestConfOfSamplesIncluded = fuzzyOR(highestConfOfSamplesIncluded, FuncBlockConfDif2StateDev->getY(deviation));
				histSampleCounter++;
			}

			//highestConfOf1Var = fuzzyOR(highestConfOf1Var, fuzzyOR(highestConfOfSamplesIncluded, FuncBlockConfDif2StateTime->getY((float)histSampleCounter)));
			highestConfOf1Var = fuzzyAND(highestConfOf1Var, fuzzyOR(highestConfOfSamplesIncluded, FuncBlockConfDif2StateTime->getY((float)histSampleCounter)));

		}
	}

	return highestConfOf1Var;
}

unsigned int SubState::getSampleHistoryLength() {
	return lSampleHistory.size();
}
