//===-- rosa/agent/ReliabilityConfidenceCombinator.h ------------*- C++ -*-===//
//
//                                 The RoSA Framework
//
// Distributed under the terms and conditions of the Boost Software License 1.0.
// See accompanying file LICENSE.
//
// If you did not receive a copy of the license file, see
// http://www.boost.org/LICENSE_1_0.txt.
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/ReliabilityConfidenceCombinator.h
///
/// \author Daniel Schnoell (daniel.schnoell@tuwien.ac.at)
///
/// \date 2019
///
/// \brief  Definition of *ReliabilityConfidenceCombinator* *functionality*.
///
/// \note based on Maximilian Goetzinger (maxgot@utu.fi) code in
/// CAM_Dirty_include SA-EWS2_Version... inside Agent.cpp
///
/// \note By defining and setting Reliability_trace_level it is possible to
/// change the level to which it should be traced. \note All classes throw
/// runtime errors if not all things are set
///
/// \note should the Reliability be capped?
///
///
//===----------------------------------------------------------------------===//

#ifndef ROSA_AGENT_ReliabilityConfidenceCombinator_H
#define ROSA_AGENT_ReliabilityConfidenceCombinator_H

#include "rosa/core/forward_declarations.h" // needed for id_t
#include "rosa/support/log.h"

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

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

/// 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 Likeliness Combinator
/// more readable \tparam IdentifierType The Data-type of the Identifiers \tparam
/// LikelinessType The Data-type of the Likeliness
/// \note this should/will be changed into a std::pair because it isn't needed
/// anymore
template <typename IdentifierType, typename LikelinessType> struct Symbol {
  /// making both Template Arguments readable to make a few things easier
  using _IdentifierType = IdentifierType;
  /// making both Template Arguments readable to make a few things easier
  using _LikelinessType = LikelinessType;

  /// The actual place where the data is stored
  IdentifierType Identifier;
  /// The actual place where the data is stored
  LikelinessType Likeliness;

  Symbol(IdentifierType _Identifier, LikelinessType _Likeliness)
      : Identifier(_Identifier), Likeliness(_Likeliness){};
  Symbol(){};

  /// 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 Symbol &c) {
    out << "Identifier: " << c.Identifier << "\t Likeliness: " << c.Likeliness
        << " ";
    return out;
  }

  /// needed or it throws an clang diagnosic error
  using map =
      std::map<IdentifierType, LikelinessType>; // needed or it throws an
                                                 // clang diagnosic error
  /// 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<Symbol> &operator<<(std::vector<Symbol> &me,
                                            map &&data) {
    for (auto tmp : data) {
      me.push_back(Symbol(tmp.first, tmp.second));
#if Reliability_trace_level <= trace_everything
      LOG_TRACE_STREAM << "\n" << Symbol(tmp.first, tmp.second) << trace_end;
#endif
    }
    return me;
  }

  /// This is to push the data inside a vector in a human readable 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<Symbol> &c) {
    std::size_t index = 0;
    for (Symbol data : c) {
      out << index << " : " << data << "\n";
      index++;
    }
    return out;
  }
};

/// This is the combinator for Reliability and confidences it takes the
/// Value, its "History" and feedback from \c
/// CrossCombinator to calculate different Reliabilities.
/// \tparam ValueType Data-type of the Value	( Typically
/// double or float) \tparam IdentifierType Data-type of the Identifier ( Typically
/// long or int)
///	\tparam ReliabilityType Data-type of the Reliability		(
/// Typically double	or float)
///
/// \note more information about how it calculates
/// the Reliabilities it should be considered feedback is a sort of Confidence
/// \verbatim
///----------------------------------------------------------------------------------
///
///
///                  ->AbsoluteReliabilityFunction---> getInputReliability()
///                  |             |
///                  |             V
///  Sensor Value ---|       ReliabilityAndConfidenceCombinator -> next line
///                  |             A					|
///                  |             |					V
///                  ->ConfidenceFunction 			getPossibleIdentifiers()
///
///-----------------------------------------------------------------------------------
///
///           feedback
///               |
///               V
///           FeedbackSymbols
///               |               -> History -------|
///               V               |                 V
/// here -> LikelinessFeedbackCombinator ------>LikelinessHistoryCombinator->next line
///                   |                                   |
///                   V                                   V
/// getpossibleIdentifiersWithMasterFeedback()      SymbolsWithHistory()
///
///----------------------------------------------------------------------------------
///
/// here -> sort -> most likely -> bestSymbol()
///
///---------------------------------------------------------------------------------
/// \endverbatim
/// the mentioned methods are early outs so if two ore more of them are run in
/// the same step they will be interpreted as different time steps
/// <pre>
/// Default values for Combinators:
///	AbsoluteAndSlopeReliabilityCombinationMethod		= combinationMin;
///	ReliabilityAndConfidenceCombinator=ReliabilityAndConfidenceCombinatorMin;
/// LikelinessFeedbackCombinator		= LikelinessFeedbackCombinatorAverage;
/// LikelinessHistoryCombinator			= LikelinessHistoryCombinatorMax;
///	</pre>
/// To understand the place where the combinator methods come into play a list
/// for each early exit and which Methods are used.
///
/// <pre>
/// \c getInputReliability():
///		-AbsoluteAndSlopeReliabilityCombinationMethod
/// \c getPossibleIdentifiers():
///		-AbsoluteAndSlopeReliabilityCombinationMethod
///		-ReliabilityAndConfidenceCombinator
/// \c getpossibleIdentifiersWithMasterFeedback():
///		-AbsoluteAndSlopeReliabilityCombinationMethod
///		-ReliabilityAndConfidenceCombinator
///		-LikelinessFeedbackCombinator
/// \c SymbolsWithHistory():
///		-AbsoluteAndSlopeReliabilityCombinationMethod
///		-ReliabilityAndConfidenceCombinator
///		-LikelinessFeedbackCombinator
///		-LikelinessHistoryCombinator
/// \c bestSymbol():
///		-AbsoluteAndSlopeReliabilityCombinationMethod
///		-ReliabilityAndConfidenceCombinator
///		-LikelinessFeedbackCombinator
///		-LikelinessHistoryCombinator
/// </pre>
template <typename ValueType, typename IdentifierType,
          typename ReliabilityType>
class ReliabilityAndConfidenceCombinator {
public:
  static_assert(std::is_arithmetic<ValueType>::value,
                "LowLevel: ValueType has to an arithmetic type\n");
  static_assert(std::is_arithmetic<IdentifierType>::value,
                "LowLevel: IdentifierType has to an arithmetic type\n");
  static_assert(std::is_arithmetic<ReliabilityType>::value,
                "LowLevel: ReliabilityType has to an arithmetic type\n");

  /// Typedef to shorten the writing.
  /// \c Symbol
  using Symbol = Symbol<IdentifierType, ReliabilityType>;

  /// Calculates the input Reliability by combining Reliability of the Sensor
  /// and the Slope Reliability \param SensorValue The sensor Value \note to set
  /// the combination method \c setAbsoluteAndSlopeReliabilityCombinationMethod()
  ReliabilityType
  getInputReliability(const ValueType &SensorValue) noexcept {
    ReliabilityType inputReliability =
        getReliability(SensorValue, previousSensorValue, timeStep);
    previousSensorValue = SensorValue;
    PreviousSensorValueExists = true;
    return inputReliability;
  }

  /// Calculates the possible Identifiers
  /// \param SensorValue the Sensor Value
  /// \brief it combines the input reliability and the confidence of the Sensor.
  /// The use combination method can be set using \c
  /// setReliabilityAndConfidenceCombinator()
  std::vector<Symbol>
  getPossibleIdentifiers(const ValueType &SensorValue) noexcept {
    std::vector<Symbol> possibleIdentifiers;
    ReliabilityType inputReliability = getInputReliability(SensorValue);

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

    possibleIdentifiers << ConfidenceFunction->operator()(SensorValue);
    possibleIdentifiers = ReliabilityAndConfidenceCombinator(
        possibleIdentifiers, inputReliability);
    return possibleIdentifiers;
  }

  /// return the Possible Values with the feedback in mind
  /// \param SensorValue The sensor Value
  /// \brief it combines the input reliability and the confidence of the Sensor.
  /// The combines them with LikelinessFeedbackCombinator and returns the result.
  std::vector<Symbol> getpossibleIdentifiersWithMasterFeedback(
      const ValueType &SensorValue) noexcept {
    std::vector<Symbol> possibleIdentifiers;
    ReliabilityType inputReliability = getInputReliability(SensorValue);

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

    possibleIdentifiers << ConfidenceFunction->operator()(SensorValue);

    possibleIdentifiers = ReliabilityAndConfidenceCombinator(
        possibleIdentifiers, inputReliability);

    possibleIdentifiers =
        LikelinessFeedbackCombinator(possibleIdentifiers, FeedbackSymbols);
    return possibleIdentifiers;
  }

  /// returns all possible Identifiers and Reliabilities with the History in
  /// mind \param SensorValue the Sensor value how this is done is described at
  /// the class.
  std::vector<Symbol> SymbolsWithHistory(
      const ValueType &SensorValue) noexcept {
    std::vector<Symbol> ActuallPossibleIdentifiers;
    std::vector<Symbol> possibleIdentifiers;
    ReliabilityType inputReliability = getInputReliability(SensorValue);

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

    possibleIdentifiers << ConfidenceFunction->operator()(SensorValue);

    possibleIdentifiers = ReliabilityAndConfidenceCombinator(
        possibleIdentifiers, inputReliability);
    possibleIdentifiers =
        LikelinessFeedbackCombinator(possibleIdentifiers, FeedbackSymbols);

    saveInHistory(possibleIdentifiers);
#if Reliability_trace_level <= trace_vectors
    LOG_TRACE_STREAM << "\nActuallPossibleIdentifiers:\n"
                     << possibleIdentifiers << trace_end;
    LOG_TRACE_STREAM << "\npossibleIdentifiers:\n"
                     << possibleIdentifiers << trace_end;
#endif
    possibleIdentifiers.clear();

    return SymbolsFromHistory();
  }

  /// Calculates the Reliability
  /// \param SensorValue The current Values of the Sensor
  ///
  /// \return Reliability and Identifier of the current SensorValue
  ///
  Symbol bestSymbol(
      const ValueType &SensorValue) noexcept {
#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<Symbol> ActuallPossibleIdentifiers;
    std::vector<Symbol> possibleIdentifiers;
    ReliabilityType inputReliability = getInputReliability(SensorValue);

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

    possibleIdentifiers << ConfidenceFunction->operator()(SensorValue);

    possibleIdentifiers = ReliabilityAndConfidenceCombinator(
        possibleIdentifiers, inputReliability);
    possibleIdentifiers =
        LikelinessFeedbackCombinator(possibleIdentifiers, FeedbackSymbols);

    saveInHistory(possibleIdentifiers);
#if Reliability_trace_level <= trace_vectors
    LOG_TRACE_STREAM << "\nActuallPossibleIdentifiers:\n"
                     << possibleIdentifiers << trace_end;
    LOG_TRACE_STREAM << "\npossibleIdentifiers:\n"
                     << possibleIdentifiers << trace_end;
#endif
    possibleIdentifiers.clear();

    possibleIdentifiers = SymbolsFromHistory();

    std::sort(possibleIdentifiers.begin(), possibleIdentifiers.end(),
              [](Symbol A, Symbol B) -> bool {
                return A.Likeliness > B.Likeliness;
              });

#if Reliability_trace_level <= trace_outputs
    LOG_TRACE_STREAM << "\noutput lowlevel: " << possibleIdentifiers.at(0)
                     << trace_end;
#endif
    return possibleIdentifiers.at(0);
  }

  /// feedback for this functionality most commonly it comes from a Master Agent
  /// \param _FeedbackSymbols The Identifiers + Reliability for the feedback
  /// \brief This input kind of resembles a confidence but not
  /// directly it more or less says: compared to the other Identifiers inside
  /// the System these are the Identifiers with the Reliability that you have.
  void feedback(
      const std::vector<Symbol>
          &_FeedbackSymbols) noexcept // it is being copied internally anyway
  {
    FeedbackSymbols = _FeedbackSymbols;
  }

  //
  // ----------------------Reliability and Confidence Function setters----------
  //
  /// This is the setter for Confidence Function
  /// \param _ConfidenceFunction A pointer to the Functional for the
  /// \c Confidence of the Sensor value
  void setConfidenceFunction(
      std::shared_ptr<RangeConfidence<ReliabilityType, IdentifierType,
                                      ValueType>> &_ConfidenceFunction) noexcept {
    ConfidenceFunction = _ConfidenceFunction;
  }

  /// This is the setter for AbsoluteReliabilityFunction 
  /// \param _AbsoluteReliabilityFunction A pointer to the Functional for the AbsoluteReliabilityFunction
  /// \brief The AbsoluteReliabilityFunction takes the current Sensor value and return the
  /// AbsoluteReliabilityFunction of the value.
  void setAbsoluteReliabilityFunction(
      std::shared_ptr<Abstraction<ValueType, ReliabilityType>>
          &_AbsoluteReliabilityFunction) noexcept {
    AbsoluteReliabilityFunction = _AbsoluteReliabilityFunction;
  }

  /// This is the setter for ReliabilitySlopeFunction Function
  /// \param _ReliabilitySlopeFunction A pointer to the Functional for the
  /// ReliabilitySlopeFunction
  /// \brief The ReliabilitySlopeFunction takes the difference of the current Sensor
  /// Value to the last one and tells you how likely the change is.
  void setReliabilitySlopeFunction(
      std::shared_ptr<Abstraction<ValueType, ReliabilityType>>
          &_ReliabilitySlopeFunction) noexcept {
    ReliabilitySlopeFunction = _ReliabilitySlopeFunction;
  }

  /// This is the setter for TimeFunctionForLikeliness Function
  /// \param _TimeFunctionForLikeliness A pointer to the Functional for the TimeFunctionForLikeliness
  /// \brief The time function takes the position in the History with greater
  /// equals older and return a Reliability of how "relevant" it is.
  void setTimeFunctionForLikelinessFunction(
      std::shared_ptr<Abstraction<std::size_t, ReliabilityType>>
          &_TimeFunctionForLikeliness) noexcept {
    TimeFunctionForLikeliness = _TimeFunctionForLikeliness;
  }

  /// This is the setter for all possible Identifiers
  /// \param Identifiers A vector containing all Identifiers
  /// \brief This exists even though \c Identifier Type is an arithmetic Type because
  /// the Identifiers do not need to be "next" to each other ( ex. Identifiers={ 1 7 24 })
  void setIdentifiers(const std::vector<IdentifierType> &Identifiers) noexcept {
    this->Identifiers = Identifiers;
  }

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

  /// This sets the Value set Counter
  /// \param timeStep the new Value
  /// \note This might actually be only an artifact. It is only used to get the
  /// reliability from the \c ReliabilitySlopeFunction [ ReliabilitySlopeFunction->operator()(
  /// (lastValue - actualValue) / (ValueType)timeStep) ]
  void setTimeStep(const unsigned int &timeStep) noexcept {
    this->timeStep = timeStep;
  }

  //
  // ----------------combinator setters-----------------------------------------
  //

  /// This sets the combination method used by the History
  /// \param Meth the method which should be used. predefined inside the  \c
  /// predefinedMethods struct LikelinessHistoryCombinator<method>()
  void setLikelinessHistoryCombinator(
      const std::function<ReliabilityType(ReliabilityType, ReliabilityType)>
          &Meth) noexcept {
    LikelinessHistoryCombinator = Meth;
  }

  /// sets the predefined method for the combination of the possible Identifiers
  /// and the master
  /// \param Meth the method which should be used. predefined inside the  \c
  /// predefinedMethods struct LikelinessFeedbackCombinator<method>()
  void setLikelinessFeedbackCombinator(
      const std::function<std::vector<Symbol>(
          std::vector<Symbol>, std::vector<Symbol>)> &Meth) noexcept {
    LikelinessFeedbackCombinator = Meth;
  }

  /// Sets the used combination method for Possible Identifiers
  /// \param Meth the method which should be used. predefined inside the  \c
  /// predefinedMethods struct setReliabilityAndConfidenceCombinator<method>()
  void setReliabilityAndConfidenceCombinator(
      const std::function<std::vector<Symbol>(
          std::vector<Symbol>, ReliabilityType)> &Meth) noexcept {
    ReliabilityAndConfidenceCombinator = Meth;
  }

  /// sets the input reliability combinator method
  /// \param method the method which should be used. predefined inside the  \c
  /// predefinedMethods struct combination<method>()
  void setAbsoluteAndSlopeReliabilityCombinationMethod(
      const std::function<ReliabilityType(ReliabilityType, ReliabilityType)>
          &&method) noexcept {
    AbsoluteAndSlopeReliabilityCombinationMethod = method;
  }

  //
  // ----------------predefined combinators------------------------------------
  //
  /// This struct is a pseudo name space to have easier access to all predefined
  /// methods while still not overcrowding the class it self
  struct predefinedMethods {
    /// predefined Method
    static ReliabilityType
    LikelinessHistoryCombinatorMin(ReliabilityType A, ReliabilityType B) noexcept {
      return std::min(A, B);
    }
    /// predefined Method
    static ReliabilityType
    LikelinessHistoryCombinatorMax(ReliabilityType A, ReliabilityType B) noexcept {
      return std::max(A, B);
    }
    /// predefined Method
    static ReliabilityType
    LikelinessHistoryCombinatorMult(ReliabilityType A, ReliabilityType B) noexcept {
      return A * B;
    }
    /// predefined Method
    static ReliabilityType
    LikelinessHistoryCombinatorAverage(ReliabilityType A,
                                   ReliabilityType B) noexcept {
      return (A + B) / 2;
    }

    /// predefined method
    static std::vector<Symbol>
    LikelinessFeedbackCombinatorAverage(std::vector<Symbol> A,
                                    std::vector<Symbol> B) noexcept {
      for (auto &tmp_me : A)
        for (auto &tmp_other : B) {
          if (tmp_me.Identifier == tmp_other.Identifier) {
            tmp_me.Likeliness =
                (tmp_me.Likeliness + tmp_other.Likeliness) / 2;
          }
        }
      return A;
    }
    /// predefined method
    static std::vector<Symbol>
    LikelinessFeedbackCombinatorMin(std::vector<Symbol> A,
                                std::vector<Symbol> B) noexcept {
      for (auto &tmp_me : A)
        for (auto &tmp_other : B) {
          if (tmp_me.Identifier == tmp_other.Identifier) {
            tmp_me.Likeliness =
                std::min(tmp_me.Likeliness + tmp_other.Likeliness);
          }
        }
      return A;
    }
    /// predefined method
    static std::vector<Symbol>
    LikelinessFeedbackCombinatorMax(std::vector<Symbol> A,
                                std::vector<Symbol> B) noexcept {
      for (auto &tmp_me : A)
        for (auto &tmp_other : B) {
          if (tmp_me.Identifier == tmp_other.Identifier) {
            tmp_me.Likeliness =
                std::max(tmp_me.Likeliness + tmp_other.Likeliness);
          }
        }
      return A;
    }
    /// predefined method
    static std::vector<Symbol>
    LikelinessFeedbackCombinatorMult(std::vector<Symbol> A,
                                 std::vector<Symbol> B) noexcept {
      for (auto &tmp_me : A)
        for (auto &tmp_other : B) {
          if (tmp_me.Identifier == tmp_other.Identifier) {
            tmp_me.Likeliness = tmp_me.Likeliness * tmp_other.Likeliness;
          }
        }
      return A;
    }

    /// Predefined combination method for possible Identifiers
    static std::vector<Symbol>
    ReliabilityAndConfidenceCombinatorMin(std::vector<Symbol> A,
                                           ReliabilityType B) noexcept {
      for (auto tmp : A)
        tmp.Likeliness = std::min(tmp.Likeliness, B);
      return A;
    }
    /// Predefined combination method for possible Identifiers
    static std::vector<Symbol>
    ReliabilityAndConfidenceCombinatorMax(std::vector<Symbol> A,
                                           ReliabilityType B) noexcept {
      for (auto tmp : A)
        tmp.Likeliness = std::max(tmp.Likeliness, B);
      return A;
    }

    /// Predefined combination method for possible Identifiers
    static std::vector<Symbol>
    ReliabilityAndConfidenceCombinatorAverage(std::vector<Symbol> A,
                                               ReliabilityType B) noexcept {
      for (auto tmp : A)
        tmp.Likeliness = (tmp.Likeliness + B) / 2;
      return A;
    }

    /// Predefined combination method for possible Identifiers
    static std::vector<Symbol>
    ReliabilityAndConfidenceCombinatorMult(std::vector<Symbol> A,
                                            ReliabilityType B) noexcept {
      for (auto tmp : A)
        tmp.Likeliness = tmp.Likeliness * B / 2;
      return A;
    }

    /// The predefined min combinator method
    static ReliabilityType combinationMin(ReliabilityType A,
                                          ReliabilityType B) noexcept {
      return std::min(A, B);
    }

    /// The predefined max combinator method
    static ReliabilityType combinationMax(ReliabilityType A,
                                          ReliabilityType B) noexcept {
      return std::max(A, B);
    }

    /// The predefined average combinator method
    static ReliabilityType combinationAverage(ReliabilityType A,
                                              ReliabilityType B) noexcept {
      return (A + B) / 2;
    }

    /// The predefined average combinator method
    static ReliabilityType combinationMult(ReliabilityType A,
                                           ReliabilityType B) noexcept {
      return A * B;
    }
  };

  // ----------------------------------------------------------------
  //			Stored Values
  // ----------------------------------------------------------------
private:
  std::vector<std::vector<Symbol>> History;
  std::size_t HistoryMaxSize;
  std::vector<Symbol> FeedbackSymbols;

  ValueType previousSensorValue;
  unsigned int timeStep;
  std::vector<IdentifierType> Identifiers;
  bool PreviousSensorValueExists = false;

  std::shared_ptr<
      RangeConfidence<ReliabilityType, IdentifierType, ValueType>>
      ConfidenceFunction;
  std::shared_ptr<Abstraction<ValueType, ReliabilityType>> AbsoluteReliabilityFunction;
  std::shared_ptr<Abstraction<ValueType, ReliabilityType>>
      ReliabilitySlopeFunction;
  std::shared_ptr<Abstraction<std::size_t, ReliabilityType>> TimeFunctionForLikeliness;

  // combination functions
  std::function<ReliabilityType(ReliabilityType, ReliabilityType)>
      AbsoluteAndSlopeReliabilityCombinationMethod = predefinedMethods::combinationMin;

  std::function<std::vector<Symbol>(std::vector<Symbol>, ReliabilityType)>
      ReliabilityAndConfidenceCombinator =
          predefinedMethods::ReliabilityAndConfidenceCombinatorMin;

  std::function<std::vector<Symbol>(std::vector<Symbol>,
                                       std::vector<Symbol>)>
      LikelinessFeedbackCombinator =
          predefinedMethods::LikelinessFeedbackCombinatorAverage;
  std::function<ReliabilityType(ReliabilityType, ReliabilityType)>
      LikelinessHistoryCombinator = predefinedMethods::LikelinessHistoryCombinatorMax;

  // ---------------------------------------------------------------------------
  //			needed Functions
  // ---------------------------------------------------------------------------

  /// returns the Reliability
  /// \param actualValue The Value of the Sensor
  /// \param lastValue of the Sensor this is stored in the class
  /// \param _timeStep It has an effect on the difference of the current
  /// and last value This might not be needed anymore
  /// \brief it returns the combination the \c Reliability function and \c
  /// ReliabilitySlopeFunction if the previous value exists. if it doesn't it only
  /// returns the \c Reliability function value.
  ReliabilityType getReliability(const ValueType &actualValue,
                                 const ValueType &lastValue,
                                 const unsigned int &_timeStep) noexcept {
    ReliabilityType relAbs = AbsoluteReliabilityFunction->operator()(actualValue);
    if (PreviousSensorValueExists) {
      ReliabilityType relSlo = ReliabilitySlopeFunction->operator()(
          (lastValue - actualValue) / (ValueType)_timeStep);

      return AbsoluteAndSlopeReliabilityCombinationMethod(relAbs, relSlo);
    } else
      return relAbs;
  }

  /// adapts the possible Identifiers by checking the History and combines those
  /// values.
  /// \brief combines the historic values with the \c TimeFunctionForLikeliness function
  /// and returns the maximum Reliability for all Identifiers.
  std::vector<Symbol> SymbolsFromHistory() noexcept {
    // iterate through all history entries
    std::size_t posInHistory = 0;
    std::vector<Symbol> symbolFromHistory; //History Symbols
    for (auto timeStep = History.begin(); timeStep < History.end();
         timeStep++, posInHistory++) {

      // iterate through all possible Identifiers of each history entry
      for (Symbol &symbol : *timeStep) {

        IdentifierType historyIdentifier = symbol.Identifier;
        ReliabilityType historyConf = symbol.Likeliness;

        historyConf = historyConf * TimeFunctionForLikeliness->operator()(posInHistory);

        bool foundIdentifier = false;
        for (Symbol &pS : symbolFromHistory) {

          if (pS.Identifier == historyIdentifier) {

            pS.Likeliness =
                LikelinessHistoryCombinator(pS.Likeliness, historyConf);

            foundIdentifier = true;
          }
        }

        if (foundIdentifier == false) {

          Symbol possibleIdentifier;	// Symbol
          possibleIdentifier.Identifier = historyIdentifier;
          possibleIdentifier.Likeliness = historyConf;

          symbolFromHistory.push_back(possibleIdentifier);
        }
      }
    }

    return symbolFromHistory;
  }

  /// saves the Identifiers in the History
  /// \brief It checks the incoming Identifiers if any have a Reliability
  /// greater than 0.5 all of them get saved inside the History and then the
  /// History get shortened to the maximal length. It only saves the Value if
  /// the History is empty.
  ///
  /// \param actualPossibleIdentifiers The Identifiers which should be saved
  ///
  /// \note Does the History really make sense if the values are to small it
  /// only stores something if it's empty and not if it isn't completely filled
  void saveInHistory(
      const std::vector<Symbol> &actualPossibleIdentifiers) noexcept { // Symbols

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

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

} // namespace agent
} // namespace rosa
#endif // !ROSA_AGENT_ReliabilityConfidenceCombinator_H
