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

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

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

#define INJECTIONPARTITIONING 10

State::State() {
	//discreteAveragePartitionSize = INJECTIONPARTITIONING;
	discreteAveragePartitionCounter = 0;
	stateIsValid = false;
}
/*
bool State::setDiscreteAveragePartitionSize(unsigned int discreteAveragePartitionSize) {
	if (discreteAveragePartitionSize > 0) {
		this->discreteAveragePartitionSize = discreteAveragePartitionSize;
		return true;
	}
	return false;
}

unsigned int State::getDiscreteAveragePartitionSize() {
	return discreteAveragePartitionSize;
}
*/
bool State::addSubState(vector<SubState*>* vSubStates, SlaveAgentSlotOfAgent* slot) {
	SubState* subState = new (nothrow) SubState();
	if (subState != NULL) {
		subState->setSlot(slot);
		//try {
			vSubStates->push_back(subState);
			return true;
		/*}
		catch (bad_alloc& error) {
			printf("bad_alloc caught: %s", error.what());
			delete subState;
		}*/
	}
	return false;
}

bool State::addInputSubState(SlaveAgentSlotOfAgent* slot) {
	return addSubState(&vInputSubStates, slot);;
}

bool State::addOutputSubState(SlaveAgentSlotOfAgent* slot) {
	return addSubState(&vOutputSubStates, slot);
}

void State::resetDiscreteAveragePartitionCounter() {
	discreteAveragePartitionCounter = 0;
}

bool State::addNewdiscreteAveragePartition() {
	bool flagWorkedForAll = true;

	for (auto &subState : vInputSubStates) {
		if (!subState->addNewDiscreteAverage())
			flagWorkedForAll = false;
	}
	for (auto &subState : vOutputSubStates) {
		if (!subState->addNewDiscreteAverage())
			flagWorkedForAll = false;
	}

	return flagWorkedForAll;
}

bool State::injectValues(unsigned int discreteAveragePartitionSize) {

	bool flagWorkedForAll = true;
	
	if (discreteAveragePartitionCounter == 0) {
		for (auto &subState : vInputSubStates) {
			subState->deleteLastDiscreteAverageBlockIfNotCompleted(discreteAveragePartitionSize);
		}
		for (auto &subState : vOutputSubStates) {
			subState->deleteLastDiscreteAverageBlockIfNotCompleted(discreteAveragePartitionSize);
		}
		flagWorkedForAll = addNewdiscreteAveragePartition();
	}

	if (flagWorkedForAll) {
		discreteAveragePartitionCounter++;
		// XXX - >= or > ??
		if (discreteAveragePartitionCounter >= discreteAveragePartitionSize) {
			discreteAveragePartitionCounter = 0;
		}

		for (auto &subState : vInputSubStates) {
			if (subState->injectValue())
				flagWorkedForAll = false;
		}
		for (auto &subState : vOutputSubStates) {
			if (subState->injectValue())
				flagWorkedForAll = false;
		}

		//printf(" >>> Inject Values (partCounter: %u)\n", discreteAveragePartitionCounter);
		//getchar();

	}

	return flagWorkedForAll;
}

bool State::injectValuesAndMakeNewDiscreteAveragePartition(unsigned int discreteAveragePartitionSize) {
	discreteAveragePartitionCounter = 0;
	return injectValues(discreteAveragePartitionSize);
}


bool State::variablesAreRelated(vector<SubState*>* vSubStates, float thresholdToBeRelated) {
	bool flagAllValuesAreRelated = true;
	
	for (auto &subState : *vSubStates) {
		if (!subState->valueIsRelated(thresholdToBeRelated)) {
			flagAllValuesAreRelated = false;
		}

	}

	return flagAllValuesAreRelated;
}

bool State::inputVariablesAreRelated(float thresholdToBeRelated) {
	return variablesAreRelated(&vInputSubStates, thresholdToBeRelated);
}

bool State::outputVariablesAreRelated(float thresholdToBeRelated) {
	return variablesAreRelated(&vOutputSubStates, thresholdToBeRelated);
}

unsigned int State::getNumOfInjections() {
	if (!vInputSubStates.empty()) {
		return vInputSubStates.front()->getNumOfInjections();
	}
	return 0;
}


bool State::checkSubStatesForNotDrifting(vector<SubState*>* vSubStates, unsigned int discreteAveragePartitionSize, /*unsigned int compareDistanceDiscreteAveragePartition,*/ float thresholdNotDrift) {
	for (auto &subState : *vSubStates) {
		if (subState->getNumberOfCompletedDiscreteAverageBlocks(discreteAveragePartitionSize) > 1) {

			//printf("completed blocks = %u\n", subState->getNumberOfCompletedDiscreteAverageBlocks(discreteAveragePartitionSize));
			//getchar();

			if (!valueIsRelatedToReferenceValue(subState->getDiscreteAverageOfFirstBlock(discreteAveragePartitionSize), subState->getDiscreteAverageOfLastBlock(discreteAveragePartitionSize), thresholdNotDrift)) {
			//if (!valueIsRelatedToReferenceValue(subState->getDiscreteAverageOfBlockBeforeLastBlock(discreteAveragePartitionSize, compareDistanceDiscreteAveragePartition), subState->getDiscreteAverageOfLastBlock(discreteAveragePartitionSize), thresholdNotDrift)) {
			
				return false;
			}
		}
	}
	//getchar();
	return true;
}


bool State::checkAllVariablesForNotDrifting(unsigned int discreteAveragePartitionSize, /*unsigned int compareDistanceDiscreteAveragePartition,*/ float thresholdNotDrift) {
	
	return checkSubStatesForNotDrifting(&vInputSubStates, discreteAveragePartitionSize, /*compareDistanceDiscreteAveragePartition,*/ thresholdNotDrift) && checkSubStatesForNotDrifting(&vOutputSubStates, discreteAveragePartitionSize, /*compareDistanceDiscreteAveragePartition,*/ thresholdNotDrift);
}

//DATE18
float State::checkSubStatesForDriftingFuzzy(vector<SubState*>* vSubStates, unsigned int discreteAveragePartitionSize, LinearFunctionBlock* Drift) {

	float confidenceDriftMax = 0;

	for (auto &subState : *vSubStates) {
		
		if (subState->getNumberOfCompletedDiscreteAverageBlocks(discreteAveragePartitionSize) > 1) {
			
			float confidenceDrift = Drift->getY(deviationValueReferenceValue(subState->getDiscreteAverageOfLastBlock(discreteAveragePartitionSize), subState->getDiscreteAverageOfFirstBlock(discreteAveragePartitionSize)));
			
			//printf("confDrift = %f, deviationValueReferenceValue = %f\n", confidenceDrift, deviationValueReferenceValue(subState->getDiscreteAverageOfLastBlock(discreteAveragePartitionSize), subState->getDiscreteAverageOfFirstBlock(discreteAveragePartitionSize)));

			if (confidenceDrift > confidenceDriftMax)
				confidenceDriftMax = confidenceDrift;
		}
	}

	return confidenceDriftMax;
}

//DATE18
float State::checkAllVariablesForDriftingFuzzy(unsigned int discreteAveragePartitionSize, LinearFunctionBlock* Drift) {

	float confidenceDriftInput = checkSubStatesForDriftingFuzzy(&vInputSubStates, discreteAveragePartitionSize, Drift);
	float confidenceDriftOutput = checkSubStatesForDriftingFuzzy(&vOutputSubStates, discreteAveragePartitionSize, Drift);

	if (confidenceDriftInput > confidenceDriftOutput)
		return confidenceDriftInput;
	else
		return confidenceDriftOutput;
}


//DATE18
float State::variablesAreRelatedFuzzy(vector<SubState*>* vSubStates, LinearFunctionBlock* SameState) {

	float confRelatedMin = 1;

	for (auto &subState : *vSubStates) {
		
		float confRelated = subState->valueIsRelatedFuzzy(SameState);
		//printf("conf %f\n", confRelated);

		if (confRelated < confRelatedMin)
			confRelatedMin = confRelated;
	}

	return confRelatedMin;
}

float State::inputVariablesAreRelatedFuzzy(LinearFunctionBlock* SameState) {
	return variablesAreRelatedFuzzy(&vInputSubStates, SameState);
}

float State::outputVariablesAreRelatedFuzzy(LinearFunctionBlock* SameState) {
	return variablesAreRelatedFuzzy(&vOutputSubStates, SameState);
}

bool State::insertValueInState(LinearFunctionBlock* FuncBlockConfValStateDev, LinearFunctionBlock* FuncBlockConfInvStateDev, LinearFunctionBlock* FuncBlockConfValStateTime, LinearFunctionBlock* FuncBlockConfInvStateTime, unsigned int historySize, unsigned int discreteAveragePartitionSize) {

	//bool insertionWorked = true;

	if (discreteAveragePartitionCounter == 0) {
		for (auto &subState : vInputSubStates)
			subState->deleteLastDiscreteAverageBlockIfNotCompleted(discreteAveragePartitionSize);
		for (auto &subState : vOutputSubStates)
			subState->deleteLastDiscreteAverageBlockIfNotCompleted(discreteAveragePartitionSize);

		//insertionWorked = addNewdiscreteAveragePartition();
		addNewdiscreteAveragePartition();
	}

	discreteAveragePartitionCounter++;

	if (discreteAveragePartitionCounter >= discreteAveragePartitionSize)
		discreteAveragePartitionCounter = 0;
	
	confValidState = 1;
	confInvalidState = 0;

	for (auto &subState : vInputSubStates) {
		//if (!(subState->insertValueInSubState(FuncBlockConfValStateDev, FuncBlockConfInvStateDev, FuncBlockConfValStateTime, FuncBlockConfInvStateTime, historySize)))
			//insertionWorked = false;
		subState->insertValueInSubState(FuncBlockConfValStateDev, FuncBlockConfInvStateDev, FuncBlockConfValStateTime, FuncBlockConfInvStateTime, historySize);
		
		confValidState = fuzzyAND(confValidState, subState->getConfidenceValidState());
		confInvalidState = fuzzyOR(confInvalidState, subState->getConfidenceInvalidState());
	}

	for (auto &subState : vOutputSubStates) {
		//if (!(subState->insertValueInSubState(FuncBlockConfValStateDev, FuncBlockConfInvStateDev, FuncBlockConfValStateTime, FuncBlockConfInvStateTime, historySize)))
			//insertionWorked = false;
		subState->insertValueInSubState(FuncBlockConfValStateDev, FuncBlockConfInvStateDev, FuncBlockConfValStateTime, FuncBlockConfInvStateTime, historySize);
		
		confValidState = fuzzyAND(confValidState, subState->getConfidenceValidState());
		confInvalidState = fuzzyOR(confInvalidState, subState->getConfidenceInvalidState());
	}

	//printf("confValidState %f\nconfInvalidState %f\n", confValidState, confInvalidState);
	//getchar();

	if (confValidState > confInvalidState) {
		//printf("VALID STATE\n");
		stateIsValid = true;
		return true;
	}

	return false;

	//return insertionWorked;
}

/*
bool State::insertValueInState(float confValid, float confInvalid, unsigned int historySize, unsigned int discreteAveragePartitionSize) {
	
	return true;
}
*/


float State::getConfInputVarAreSim2State(LinearFunctionBlock* FuncBlockConfSim2StateDev, LinearFunctionBlock* FuncBlockConfSim2StateTime) {
	return getConfVarAreSim2State(&vInputSubStates, FuncBlockConfSim2StateDev, FuncBlockConfSim2StateTime);
}

float State::getConfInputVarAreDif2State(LinearFunctionBlock* FuncBlockConfDif2StateDev, LinearFunctionBlock* FuncBlockConfDif2StateTime) {
	return getConfVarAreDif2State(&vInputSubStates, FuncBlockConfDif2StateDev, FuncBlockConfDif2StateTime);
}

float State::getConfOutputVarAreSim2State(LinearFunctionBlock* FuncBlockConfSim2StateDev, LinearFunctionBlock* FuncBlockConfSim2StateTime) {
	return getConfVarAreSim2State(&vOutputSubStates, FuncBlockConfSim2StateDev, FuncBlockConfSim2StateTime);
}

float State::getConfOutputVarAreDif2State(LinearFunctionBlock* FuncBlockConfDif2StateDev, LinearFunctionBlock* FuncBlockConfDif2StateTime) {
	return getConfVarAreDif2State(&vOutputSubStates, FuncBlockConfDif2StateDev, FuncBlockConfDif2StateTime);
}


unsigned int State::getLengthOfHistory() {
	if (!vInputSubStates.empty()) {
		//printf("historyLength: %u\n", vInputSubStates.front()->getSampleHistoryLength());

		return vInputSubStates.front()->getSampleHistoryLength();
	}
	return 0;
}

bool State::isStateValid() {
	return stateIsValid;
}

float State::getConfStateValid() {
	return confValidState;
}

float State::getConfStateInvalid() {
	return confInvalidState;
}


//new
float State::getConfVarAreSim2State(vector<SubState*>* vSubStates, LinearFunctionBlock* FuncBlockConfSim2StateDev, LinearFunctionBlock* FuncBlockConfSim2StateTime) {

	float lowestConfOfAllVarAreRelated = 1;

	for (auto &subState : *vSubStates) 
		lowestConfOfAllVarAreRelated = fuzzyAND(lowestConfOfAllVarAreRelated, subState->getConfVarIsSim2State(FuncBlockConfSim2StateDev, FuncBlockConfSim2StateTime));

	return lowestConfOfAllVarAreRelated;
}

float State::getConfVarAreDif2State(vector<SubState*>* vSubStates, LinearFunctionBlock* FuncBlockConfDif2StateDev, LinearFunctionBlock* FuncBlockConfDif2StateTime) {
	float highestConfOfAllVarAreNotRelated = 0;

	for (auto &subState : *vSubStates)
		highestConfOfAllVarAreNotRelated = fuzzyOR(highestConfOfAllVarAreNotRelated, subState->getConfVarIsDif2State(FuncBlockConfDif2StateDev, FuncBlockConfDif2StateTime));

	return highestConfOfAllVarAreNotRelated;
}


















/*
bool State :: setInjectionPartitioning(unsigned int injectionPartitioning) {
	if (injectionPartitioning > 0) {
		this->injectionPartitioning = injectionPartitioning;
		return true;
	}
	return false;
}

unsigned int State :: getInjectionPartitioning() {
	return injectionPartitioning;
}

bool State :: addDiscreteAveragePartition() {
	AverageValue* avg = new AverageValue();
	if (avg != NULL) {
		try {
			vDiscreteAveragePartition.push_back(avg);
			return true;
		}
		catch (bad_alloc& error) {
			printError("bad_alloc caught: ", error.what());
			delete avg;
		}
	}
	return false;
}

bool State :: injectValue(float value) {

	AverageValue* avg = NULL;

	continuousStatisticValue.injectAndCalculateExtremeValue(value);
	//injectionCounter++; 

	if (injectionPartitionCounter == 0) {
		if (addDiscreteAveragePartition()) {
			injectionPartitionCounter++;
			avg = vDiscreteAveragePartition.back();
		}
	}
	else {
		avg = vDiscreteAveragePartition.back();
	}

	if (avg != NULL) {
		avg->injectAndCalculateAverageValue(value);
		if (injectionPartitionCounter > injectionPartitioning) {
			injectionPartitionCounter = 0;
		}
		return true;
	}
	return false;
}

bool State :: valueIsRelated(float value, float thresholdToAverage) {
	float diff;
	float avg = continuousStatisticValue.getAverageValue();

	printf("value: %f, avg: %f, th: %f\n", value, avg, thresholdToAverage);

	if (value > avg)
		diff = value - avg;
	else
		diff = avg - value;

	if (diff / avg <= thresholdToAverage)
		return true;

	return false;
}

bool State :: isNew() {
	if (continuousStatisticValue.getInjectedValuesCounter() == 0)
		return true;
	return false;
}

unsigned int State :: getNumberOfInjections() {
	return continuousStatisticValue.getInjectedValuesCounter();
}

void State :: deleteState() {
	vDiscreteAveragePartition.swap(vDiscreteAveragePartition);
}

*/