//===-- rosa/agent/Reliability.h --------------------------------*- C++ -*-===//
//
//                                 The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/Reliability.h
///
/// \author Daniel Schnoell (danielschnoell@tuwien.ac.at)
///
/// \date 2019
///
/// \brief  Definition of *reliability* *functionality*.
///
/// \note All classes throw runtime errors if not all things are set
///
//===----------------------------------------------------------------------===//

#ifndef ROSA_AGENT_RELIABILITY_H
#define ROSA_AGENT_RELIABILITY_H

#include "rosa/agent/FunctionAbstractions.hpp"
#include "rosa/agent/Functionality.h"
#include "rosa/agent/RangeConfidence.hpp"
#include "rosa/agent/CrossReliability.h"

#include <vector>
#include <algorithm>
#include <type_traits>

/// 0 everything
/// 1 vectors
/// 2 outputs
#define trace_everything 0
#define trace_vectors 1
#define trace_outputs 2

#ifndef Reliability_trace_level
	#define Reliability_trace_level 0
#endif 
#define trace_end "\n\n\n"


namespace rosa {
	namespace agent {
		/// This is a struct with a few methods that make lowlevel Reliability more readable
		/// \tparam StateType The datatype of the States
		/// \tparam ReliabilityType The datatype of the Reliability
		template< typename StateType,typename ReliabilityType>
		struct ConfOrRel {
			typedef StateType _StateType;
			typedef ReliabilityType _ReliabilityType;

			StateType score;
			ReliabilityType Reliability;

			ConfOrRel(StateType _score, ReliabilityType _Reliability) :score(_score), Reliability(_Reliability) {};
			ConfOrRel() {};

			/// Pushes the Data in a Human readable form
			/// \param out The stream where it is written to
			/// \param c The struct itself
			friend std::ostream& operator << (std::ostream& out, const ConfOrRel& c)
			{
				out << "Score: " << c.score << "\t Reliability: " << c.Reliability << " ";
				return out;
			}

			typedef std::map<StateType,ReliabilityType> map;	// needed or it throws an clang diagnosic erroor
			/// Filles the vector with the data inside the map
			/// \param me The vector to be filled
			/// \param data The data wich is to be pushed into the vector 
			friend std::vector<ConfOrRel>& operator<<(std::vector<ConfOrRel> &me, map&& data)
			{
				for (auto tmp : data)
				{
					me.push_back(ConfOrRel(tmp.first, tmp.second));
#if Reliability_trace_level<=trace_everything
					LOG_TRACE_STREAM << "\n" << ConfOrRel(tmp.first, tmp.second) << trace_end;
#endif
				}
				return me;
			}

			/// This adds the Reliabilities of the same Scores
			/// \param me The vector to wich is written to
			/// \param other The other data vector
			friend std::vector<ConfOrRel> operator+=(std::vector<ConfOrRel> &me, std::vector<ConfOrRel> other)
			{
				static_assert(std::is_arithmetic<ReliabilityType>::value);
				for (auto tmp_me:me)
					for (auto tmp_other:other) {
						if (tmp_me.score == tmp_other.score) {
							tmp_me.Reliability =tmp_me.Reliability + tmp_other.Reliability;
						}
					}
				return me;
			}

			/// This is to push the data inside a vector in a humanreadable way into the ostream
			/// \param out The ostream
			/// \param c The vector which is read
			friend std::ostream& operator << (std::ostream& out, const std::vector<ConfOrRel>& c)
			{
				std::size_t index=0;
				for(ConfOrRel data:c)
				{
					out << index << " : " << data << "\n"; 
					index++;
				}
				return out;
			}
		};

		/// This calculates the minimum of the Reliabilities & the given value
		/// \param me The vector with the Reliabilites
		/// \param value The comparing value
		template<typename Conf>
		std::vector<Conf> min(std::vector<Conf> me,typename Conf::_ReliabilityType value )
		{
			static_assert(std::is_arithmetic<typename Conf::_ReliabilityType>::value);
			for (auto tmp:me)
				tmp.Reliability = std::min(tmp.Reliability,value);
			return me;
		}




		/// This is the Reliability Functionality for a low level Agent
		/// \tparam SensorValueType Datatype of the Sensor value	( Typically double	or float)
		/// \tparam StateType		Datatype of the State			( Typically long	or int)
		///	\tparam ReliabilityType Datatype of the Reliability		( Typically double	or float)
		///
		/// use the () operator to get the reliability and feed the information from the master back to this
		/// \note all pointer for the functionalities will be deleted when this is object ist destroyed
		template <typename SensorValueType, typename StateType, typename ReliabilityType>
		class LowLevel {
		public:

			typedef ConfOrRel<StateType,ReliabilityType> ConfOrRel;

			/// Calculates the Conf/ Reliability
			/// \param SensorValue The current Values of the Sensor
			/// 
			/// \return Reliability of the current Value
			ConfOrRel operator()(SensorValueType SensorValue) {
#if Reliability_trace_level<=trace_outputs
				LOG_TRACE_STREAM << "\nTrace level is set to: " << Reliability_trace_level <<"\n"
				<< "Will trace: " << 
				((Reliability_trace_level==trace_outputs)?
					"outputs"	:
					(Reliability_trace_level==trace_vectors)?
						"vectors"	:
						(Reliability_trace_level==trace_everything)?
						"everything"	: "undefined" 
				)
				<<trace_end;
#endif

				std::vector<ConfOrRel> 	ActuallPossibleScores;
				std::vector<ConfOrRel> 	possibleScores;
				ReliabilityType 	inputReliability	=getRelibility(SensorValue, previousSensorValue, valueSetCounter);


#if Reliability_trace_level<=trace_vectors
				LOG_TRACE_STREAM << "\ninput Rel: " << inputReliability << trace_end;
#endif


				possibleScores << Confidence->operator()(SensorValue);

				
				possibleScores =  min(possibleScores,inputReliability);
				
				possibleScores += ValuesFromMaster;

				saveInHistory(possibleScores);
#if Reliability_trace_level<=trace_vectors
				LOG_TRACE_STREAM << "\nActuallPossibleScores:\n" << possibleScores << trace_end;
				LOG_TRACE_STREAM << "\npossibleScores:\n" << possibleScores << trace_end;
#endif
				possibleScores.clear();


				possibleScores = getAllPossibleScoresBasedOnHistory();

				std::sort(possibleScores.begin(), possibleScores.end(), 
				[](ConfOrRel A, ConfOrRel B)-> bool {
					static_assert(std::is_arithmetic<ReliabilityType>::value);
					return A.Reliability > B.Reliability; }
				);


				previousSensorValue = SensorValue;
				PreviousSensorValueExists = true;
				
#if Reliability_trace_level<=trace_outputs
				LOG_TRACE_STREAM << "\noutput lowlevel: " << possibleScores.at(0) << trace_end;
#endif
				return possibleScores.at(0);

			}

			/// Needed feedback from the Master
			/// \param ValuesFromMaster The Scores + Reliability from the Master for this Agent
			void feedback(std::vector<ConfOrRel> ValuesFromMaster)
			{
				this->ValuesFromMaster = ValuesFromMaster;
			}


			/// This is the setter for Confidence Function
			/// \param Confidence A pointer to the Functional for the Confidence
			void setConfidenceFunction(std::unique_ptr<RangeConfidence<ReliabilityType, StateType, SensorValueType>> &Confidence)
			{
				this->Confidence = std::move(Confidence);
			}

			/// This is the setter for Reliability Function
			/// \param Reliability A pointer to the Functional for the Reliability
			void setReliabilityFunction(std::unique_ptr<Abstraction<SensorValueType, ReliabilityType>> &Reliability)
			{
				this->Reliability = std::move(Reliability);
			}

			/// This is the setter for ReliabilitySlope Function
			/// \param ReliabilitySlope A pointer to the Functional for the ReliabilitySlope 
			void setReliabilitySlopeFunction(std::unique_ptr<Abstraction<SensorValueType, ReliabilityType>> &ReliabilitySlope)
			{
				this->ReliabilitySlope = std::move(ReliabilitySlope);
			}

			/// This is the setter for TimeConfidence Function
			/// \param TimeConfidence A pointer to the Functional for the TimeConfidence
			void setTimeConfidenceFunction(std::unique_ptr<Abstraction<std::size_t, ReliabilityType>> &TimeConfidence)
			{
				this->TimeConfidence = std::move(TimeConfidence);
			}

			/// This is the setter for all possible States
			/// \param states A vertor for all states
			void setStates(std::vector<StateType> states)
			{
				this->States=states;
			}

			/// This sets the Maximum length of the Histpry
			/// \param length The length
			void setHistoryLength(std::size_t length)
			{
				this->HistoryMaxSize=length;
			}

			/// This sets the Value set Counter			
			/// \param ValueSetCounter the new Value
			void setValueSetCounter(unsigned int ValueSetCounter)
			{
				this->valueSetCounter=ValueSetCounter;
			}

		private:
			std::vector<std::vector<ConfOrRel>> History;
			std::size_t HistoryMaxSize;
			std::vector<ConfOrRel> ValuesFromMaster;

			SensorValueType previousSensorValue;
			unsigned int valueSetCounter;
			std::vector<StateType> States;
			bool PreviousSensorValueExists = false;

			std::unique_ptr<RangeConfidence<ReliabilityType, StateType, SensorValueType>> 	Confidence;
			std::unique_ptr<Abstraction<SensorValueType, ReliabilityType>> 			Reliability;
			std::unique_ptr<Abstraction<SensorValueType, ReliabilityType>> 			ReliabilitySlope;
			std::unique_ptr<Abstraction<std::size_t, ReliabilityType>> 			TimeConfidence;

			/*--------------------------------- needed Funktions -----------------------------------------------------*/

			/// returns the Reliability 
			/// \param actualValue The Value of the Sensor
			/// \param lastValue of the Sensor this is stored in the class
			/// \param valueSetCounter It has an effect on the difference of the current and last value This might not be needed anymore
			ReliabilityType getRelibility(SensorValueType actualValue, SensorValueType lastValue, unsigned int valueSetCounter) 
			{
				static_assert(std::is_arithmetic<ReliabilityType>::value);
				ReliabilityType relAbs = Reliability->operator()(actualValue);
				if (PreviousSensorValueExists)
				{
					ReliabilityType relSlo = ReliabilitySlope->operator()((lastValue - actualValue) / (SensorValueType)valueSetCounter);

					// calculate signal input reliability
					// NOTE: options would be multiply, average, AND (best to worst:
					// average = AND > multiply) rel = relAbs * relSlo; rel = (relAbs +
					// relSlo)/2;

					return std::min(relAbs, relSlo);
				}
				else
					return relAbs;
			}

			/// adabts the possible Scores by checking the History and combines those values currently with max
			std::vector<ConfOrRel> getAllPossibleScoresBasedOnHistory() 
			{
				//iterate through all history entries
				std::size_t posInHistory = 0;
				std::vector<ConfOrRel> possibleScores;
				for (auto pShE = History.begin(); pShE < History.end(); pShE++, posInHistory++) {


					//iterate through all possible scores of each history entry
					for (ConfOrRel& pSh : *pShE) {

						StateType historyScore = pSh.score;
						ReliabilityType historyConf = pSh.Reliability;

						//combine each history score with the confidence of time
						//NOTE: multiplication, AND, or average would be alternatives (best to worst: multiplication = AND = average)
						historyConf = historyConf * TimeConfidence->operator()(posInHistory);
						//historyConf = (historyConf + TimeConfidence(posInHistory)) / 2;
						//historyConf = std::min(historyConf, TimeConfidence(posInHistory));


						bool foundScore = false;
						for (ConfOrRel& pS : possibleScores) {

							if (pS.score == historyScore) {

								//calculate confidence for score
								//NOTE: multiplication, AND, or average would be alternatives (best to worst: AND >> average = multiplication )
								//pS->confOrRel = pS->confOrRel * historyConf;
								//pS->confOrRel = (pS->confOrRel + historyConf) / 2;
								pS.Reliability = std::max(pS.Reliability, historyConf);

								foundScore = true;
							}
						}

						if (foundScore == false) {

							ConfOrRel possibleScore;
							possibleScore.score = historyScore;
							possibleScore.Reliability = historyConf;

							possibleScores.push_back(possibleScore);

						}
					}
				}

				return possibleScores;
			}

			/// saves the Scores in the History 
			/// \param actualPossibleScores The Scores which should be saved
			///
			/// \note Does the History realy make sence if the values are to smal it only stores something if its empty and not if it isn't completly filled
			void saveInHistory(std::vector<ConfOrRel> actualPossibleScores) {

				//check if the reliability of at least one possible score is high enough
				bool atLeastOneRelIsHigh = false;
				for (ConfOrRel pS : actualPossibleScores) {
					if (pS.Reliability > 0.5) {
						atLeastOneRelIsHigh = true;
					}
				}
				//save possible scores if at least one possible score is high enough (or if the history is empty)
				if (History.size() < 1 || atLeastOneRelIsHigh == true) {
					History.insert(History.begin(),actualPossibleScores);

					//if history size is higher than allowed, savo oldest element
					while (History.size() > HistoryMaxSize) {
						//delete possibleScoreHistory.back();
						History.pop_back();
					}
				}
			}

		};



		/// This is the Reliability Functionality for the Highlevel Agent
		/// \tparam StateType		Datatype of the State			( Typically	double	or float)
		/// \tparam ReliabilityType	Datatype of the	Reliability		( Typically	long	or int)
		/// 
		/// use the () operator to calculate the Reliability and all cross confidences for all slaves
		/// \note all pointer to Funcionalities get deleted upon deletion of the object
		template<typename StateType, typename ReliabilityType>
		class HighLevel
		{
		public:

			typedef ConfOrRel<StateType,ReliabilityType> ConfOrRel;

			struct returnType {
				ReliabilityType CrossReliability;
				std::map<id_t, std::vector<ConfOrRel>> CrossConfidence;
			};

			returnType operator()(std::vector<std::tuple<id_t, StateType, ReliabilityType>> &Values)
			{

				StateType EWS = 0;
				ReliabilityType combinedInputRel = 1;
				ReliabilityType combinedCrossRel = 1;
				ReliabilityType outputReliability;

				std::vector<std::pair<id_t, StateType>> Agents;
				std::map<id_t, std::vector<ConfOrRel>> 	output;
				std::vector<ConfOrRel> 			output_temporary;

				for (auto tmp : Values)
				{
					std::pair<id_t, StateType> tmp2;
					tmp2.first = std::get<0>(tmp);
					tmp2.second = std::get<1>(tmp);
					Agents.push_back(tmp2);
				}


				for (auto Value : Values) {
					id_t	id = std::get<0>(Value);
					StateType sc = std::get<1>(Value);
					ReliabilityType rel = std::get<2>(Value);

					EWS = EWS + sc;

					combinedInputRel = std::min(combinedInputRel, rel);


					//calculate the cross reliability for this slave agent
					ReliabilityType realCrossReliabilityOfSlaveAgent = CrossReliability->operator()({id,sc }, Agents); //AVERAGE, MULTIPLICATION, CONJUNCTION (best to worst: AVERAGE = CONJUNCTION > MULTIPLICATION >> )

					output_temporary.clear();
					for (StateType thoScore:States[id]) {
						//calculate the cross reliability for this slave agent
						ConfOrRel data;
						data.score = thoScore;
						data.Reliability = CrossConfidence->operator()(id, thoScore, Agents);
						output_temporary.push_back(data);
					}

					output.insert({ std::get<0>(Value),output_temporary });
					combinedCrossRel = std::min(combinedCrossRel, realCrossReliabilityOfSlaveAgent);

				}

				//combine cross reliabilites and input reliabilites of all slave agents
				//NOTE: options would be multiply, average, AND (best to worst: )
				//outputReliability = combinedInputRel * combinedCrossRel;
				//outputReliability = (combinedInputRel + combinedCrossRel) / 2;
				outputReliability = std::min(combinedInputRel, combinedCrossRel);
				return { outputReliability,output };
			}

			/// This is the setter for CrossReliability Function
			/// \param CrossReliability A pointer to the Functional for the CrossReliability
			void setFunction(std::unique_ptr<CrossReliability<StateType, ReliabilityType>> &CrossReliability)
			{
				this->CrossReliability = std::move(CrossReliability);
			}
			
			/// This is the setter for CrossConfidence Function
			/// \param CrossConfidence A pointer to the Functional for the CrossConfidence
			void setFunction(std::unique_ptr<CrossConfidence <StateType, ReliabilityType>> &CrossConfidence)
			{
				this->CrossConfidence = std::move(CrossConfidence);
			}

			/// This the adder for the states
			/// \param States id spezific states this will be copied
			void addStates( id_t id, std::vector<StateType> States)
			{
				this->States.insert({id,States});
			}

		private:

			std::unique_ptr<CrossReliability<StateType, ReliabilityType>> CrossReliability;
			std::unique_ptr<CrossConfidence <StateType, ReliabilityType>> CrossConfidence ;

			std::map<id_t,std::vector<StateType>> States;


		};
	} // namespace agent
}// namespace rosa
#endif // !ROSA_AGENT_RELIABILITY_H
