//===-- rosa/agent/SignalState.hpp ------------------------------*- C++ -*-===//
//
//                                 The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/SignalState.hpp
///
/// \author Maximilian Götzinger (maximilian.goetzinger@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Definition of *signal state* *functionality*.
///
//===----------------------------------------------------------------------===//

#ifndef ROSA_AGENT_SIGNALSTATE_HPP
#define ROSA_AGENT_SIGNALSTATE_HPP

#include "rosa/agent/FunctionAbstractions.hpp"
#include "rosa/agent/Functionality.h"
#include "rosa/agent/History.hpp"
#include "rosa/agent/State.hpp"

#include "rosa/support/math.hpp"

namespace rosa {
namespace agent {

/// Signal properties defining the properties of the signal which is monitored
/// by \c rosa::agent::SignalStateDetector and is saved in \c
/// rosa::agent::SignalStateInformation.
enum SignalProperties : uint8_t {
  INPUT = 0, ///< The signal is an input signal
  OUTPUT = 1 ///< The signal is an output signal
};

/// TODO: write description
template <typename CONFDATATYPE>
struct SignalStateInformation : StateInformation<CONFDATATYPE> {
  // Make sure the actual type arguments are matching our expectations.
  STATIC_ASSERT((std::is_arithmetic<CONFDATATYPE>::value),
                "confidence type is not to arithmetic");

  /// ConfidenceOfMatchingState is the confidence how good the new sample
  /// matches the state.
  CONFDATATYPE ConfidenceOfMatchingState;

  /// ConfidenceOfMatchingState is the confidence how bad the new sample
  /// matches the state.
  CONFDATATYPE ConfidenceOfMismatchingState;

  /// The SignalProperty saves whether the monitored signal is an input our
  /// output signal.
  SignalProperties SignalProperty;

  /// The SignalStateIsValid saves the number of samples which have been
  /// inserted into the state after entering it.
  uint32_t NumberOfInsertedSamplesAfterEntrance;

public:
  SignalStateInformation(unsigned int SignalStateID,
                         SignalProperties _SignalProperty) {
    this->StateID = SignalStateID;
    this->SignalProperty = _SignalProperty;
    this->StateCondition = StateConditions::UNKNOWN;
    this->NumberOfInsertedSamplesAfterEntrance = 0;
    this->StateIsValid = false;
    this->StateJustGotValid = false;
    this->StateIsValidAfterReentrance = false;
    this->ConfidenceStateIsValid = 0;
    this->ConfidenceStateIsInvalid = 0;
    this->ConfidenceStateIsStable = 0;
    this->ConfidenceStateIsDrifting = 0;
  }

  SignalStateInformation() = default;
};

/// \tparam INDATATYPE type of input data, \tparam CONFDATATYPE type of
/// data in that the confidence values are given, \tparam PROCDATATYPE type of
/// the relative distance and the type of data in which DABs are saved.
template <typename INDATATYPE, typename CONFDATATYPE, typename PROCDATATYPE>
class SignalState : public Functionality {

  // Make sure the actual type arguments are matching our expectations.
  STATIC_ASSERT((std::is_arithmetic<INDATATYPE>::value),
                "input data type not arithmetic");
  STATIC_ASSERT((std::is_arithmetic<CONFDATATYPE>::value),
                "confidence data type is not to arithmetic");
  STATIC_ASSERT(
      (std::is_arithmetic<PROCDATATYPE>::value),
      "process data type (DAB and Relative Distance) is not to arithmetic");

public:
  // For the convinience to write a shorter data type name
  using PartFuncReference = PartialFunction<INDATATYPE, CONFDATATYPE> &;
  // using PartFuncReference2 = ;
  using StepFuncReference = StepFunction<INDATATYPE, CONFDATATYPE> &;

private:
  /// SignalStateInfo is a struct of SignalStateInformation that contains
  /// information about the current signal state.
  SignalStateInformation<CONFDATATYPE> SignalStateInfo;

  /// The FuzzyFunctionSampleMatches is the fuzzy function that gives the
  /// confidence how good the new sample matches another sample in the sample
  /// history.
  PartFuncReference FuzzyFunctionSampleMatches;

  /// The FuzzyFunctionSampleMismatches is the fuzzy function that gives the
  /// confidence how bad the new sample matches another sample in the sample
  /// history.
  PartFuncReference FuzzyFunctionSampleMismatches;

  /// The FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the
  /// confidence how many samples from the sampe history match the new sample.
  StepFuncReference FuzzyFunctionNumOfSamplesMatches;

  /// The FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives
  /// the confidence how many samples from the sampe history mismatch the new
  /// sample.
  StepFuncReference FuzzyFunctionNumOfSamplesMismatches;

  /// The FuzzyFunctionSampleValid is the fuzzy function that gives the
  /// confidence how good one matches another sample in the sample
  /// history. This is done to evaluate whether a state is valid.
  PartFuncReference FuzzyFunctionSampleValid;

  /// The FuzzyFunctionSampleInvalid is the fuzzy function that gives the
  /// confidence how bad one sample matches another sample in the sample
  /// history. This is done to evaluate whether a state is invalid.
  PartFuncReference FuzzyFunctionSampleInvalid;

  /// The FuzzyFunctionNumOfSamplesValid is the fuzzy function that gives the
  /// confidence how many samples from the sample history match another sample.
  /// This is done to evaluate whether a state is valid.
  StepFuncReference FuzzyFunctionNumOfSamplesValid;

  /// The FuzzyFunctionNumOfSamplesInvalid is the fuzzy function that gives
  /// the confidence how many samples from the sample history mismatch another
  /// sample. This is done to evaluate whether a state is invalid.
  StepFuncReference FuzzyFunctionNumOfSamplesInvalid;

  /// The FuzzyFunctionSignalIsDrifting is the fuzzy function that gives the
  /// confidence how likely it is that the signal (resp. the state of a signal)
  /// is drifting.
  PartFuncReference FuzzyFunctionSignalIsDrifting;

  /// The FuzzyFunctionSignalIsStable is the fuzzy function that gives the
  /// confidence how likely it is that the signal (resp. the state of a signal)
  /// is stable (not drifting).
  PartFuncReference FuzzyFunctionSignalIsStable;

  /// TODO: description
  // PartialFunction<uint32_t, float> &FuzzyFunctionSignalConditionLookBack;
  /// TODO: description
  // PartialFunction<uint32_t, float>
  // &FuzzyFunctionSignalConditionHistoryDesicion;
  /// TODO: description
  // uint32_t DriftLookbackRange;

  /// SampleHistory is a history in that the last sample values are stored.
  DynamicLengthHistory<INDATATYPE, HistoryPolicy::FIFO> SampleHistory;
  /// DAB is a (usually) small history of the last sample values of which a
  /// average is calculated if the DAB is full.
  DynamicLengthHistory<INDATATYPE, HistoryPolicy::SRWF> DAB;
  /// DABHistory is a history in that the last DABs (to be exact, the averages
  /// of the last DABs) are stored.
  DynamicLengthHistory<PROCDATATYPE, HistoryPolicy::LIFO> DABHistory;

  /// LowestConfidenceMatchingHistory is a history in that the lowest confidence
  /// for the current sample matches all history samples are saved.
  DynamicLengthHistory<INDATATYPE, HistoryPolicy::FIFO>
      LowestConfidenceMatchingHistory;
  /// HighestConfidenceMatchingHistory is a history in that the highest
  /// confidence for the current sample matches all history samples are saved.
  DynamicLengthHistory<INDATATYPE, HistoryPolicy::FIFO>
      HighestConfidenceMismatchingHistory;

  /// TempConfidenceMatching is the confidence how good a sample matches the
  /// state. However, the value of this variable is only needed temporarly.
  CONFDATATYPE TempConfidenceMatching = 0;

  /// TempConfidenceMatching is the confidence how bad a sample matches the
  /// state. However, the value of this variable is only needed temporarly.
  CONFDATATYPE TempConfidenceMismatching = 0;

public:
  /// Creates an instance by setting all parameters
  /// \param SignalStateID The Id of the SignalStateinfo \c
  /// SignalStateInformation.
  ///
  /// \param FuzzyFunctionSampleMatches The FuzzyFunctionSampleMatches is the
  /// fuzzy function that gives the confidence how good the new sample matches
  /// another sample in the sample history.
  ///
  /// \param FuzzyFunctionSampleMismatches The FuzzyFunctionSampleMismatches is
  /// the fuzzy function that gives the confidence how bad the new sample
  /// matches another sample in the sample history.
  ///
  /// \param FuzzyFunctionNumOfSamplesMatches The
  /// FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the
  /// confidence how many samples from the sampe history match the new sample.
  ///
  /// \param FuzzyFunctionNumOfSamplesMismatches The
  /// FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives the
  /// confidence how many samples from the sampe history mismatch the new
  /// sample.
  ///
  /// \param FuzzyFunctionSignalIsDrifting The FuzzyFunctionSignalIsDrifting is
  /// the fuzzy function that gives the confidence how likely it is that the
  /// signal (resp. the state of a signal) is drifting.
  ///
  /// \param FuzzyFunctionSignalIsStable The FuzzyFunctionSignalIsStable is the
  /// fuzzy function that gives the confidence how likely it is that the signal
  /// (resp. the state of a signal) is stable (not drifting).
  ///
  /// \param SampleHistorySize Size of the Sample History \c
  /// DynamicLengthHistory . SampleHistory is a history in that the last sample
  /// values are stored.
  ///
  /// \param DABSize Size of DAB \c DynamicLengthHistory . DAB is a (usually)
  /// small history of the last sample values of which a average is calculated
  /// if the DAB is full.
  ///
  /// \param DABHistorySize Size of the DABHistory \c DynamicLengthHistory .
  /// DABHistory is a history in that the last DABs (to be exact, the averages
  /// of the last DABs) are stored.
  ///
  SignalState(
      uint32_t SignalStateID, SignalProperties SignalProperty,
      uint32_t SampleHistorySize, uint32_t DABSize, uint32_t DABHistorySize,
      PartFuncReference FuzzyFunctionSampleMatches,
      PartFuncReference FuzzyFunctionSampleMismatches,
      StepFuncReference FuzzyFunctionNumOfSamplesMatches,
      StepFuncReference FuzzyFunctionNumOfSamplesMismatches,
      PartFuncReference FuzzyFunctionSampleValid,
      PartFuncReference FuzzyFunctionSampleInvalid,
      StepFuncReference FuzzyFunctionNumOfSamplesValid,
      StepFuncReference FuzzyFunctionNumOfSamplesInvalid,
      PartFuncReference FuzzyFunctionSignalIsDrifting,
      PartFuncReference FuzzyFunctionSignalIsStable //,
      // PartialFunction<uint32_t, float> &FuzzyFunctionSignalConditionLookBack,
      // PartialFunction<uint32_t, float>
      //    &FuzzyFunctionSignalConditionHistoryDesicion,
      // uint32_t DriftLookbackRange
      ) noexcept
      : SignalStateInfo{SignalStateID, SignalProperty},
        FuzzyFunctionSampleMatches(FuzzyFunctionSampleMatches),
        FuzzyFunctionSampleMismatches(FuzzyFunctionSampleMismatches),
        FuzzyFunctionNumOfSamplesMatches(FuzzyFunctionNumOfSamplesMatches),
        FuzzyFunctionNumOfSamplesMismatches(
            FuzzyFunctionNumOfSamplesMismatches),
        FuzzyFunctionSampleValid(FuzzyFunctionSampleValid),
        FuzzyFunctionSampleInvalid(FuzzyFunctionSampleInvalid),
        FuzzyFunctionNumOfSamplesValid(FuzzyFunctionNumOfSamplesValid),
        FuzzyFunctionNumOfSamplesInvalid(FuzzyFunctionNumOfSamplesInvalid),
        FuzzyFunctionSignalIsDrifting(FuzzyFunctionSignalIsDrifting),
        FuzzyFunctionSignalIsStable(FuzzyFunctionSignalIsStable),
        // FuzzyFunctionSignalConditionLookBack(
        //    FuzzyFunctionSignalConditionLookBack),
        // FuzzyFunctionSignalConditionHistoryDesicion(
        //    FuzzyFunctionSignalConditionHistoryDesicion),
        // DriftLookbackRange(DriftLookbackRange),
        SampleHistory(SampleHistorySize), DAB(DABSize),
        DABHistory(DABHistorySize),
        LowestConfidenceMatchingHistory(SampleHistorySize),
        HighestConfidenceMismatchingHistory(SampleHistorySize) {}

  /// Destroys \p this object.
  ~SignalState(void) = default;

  void leaveSignalState(void) noexcept {
    DAB.clear();
    SignalStateInfo.NumberOfInsertedSamplesAfterEntrance = 0;
    SignalStateInfo.StateIsValidAfterReentrance = false;
  }

  SignalStateInformation<CONFDATATYPE>
  insertSample(INDATATYPE Sample) noexcept {

    SignalStateInfo.NumberOfInsertedSamplesAfterEntrance++;

    validateSignalState(Sample);

    SampleHistory.addEntry(Sample);

    DAB.addEntry(Sample);
    if (DAB.full()) {
      // Experiment -> exchanged next line with the folowings
      // PROCDATATYPE AvgOfDAB = DAB.template average<PROCDATATYPE>();

      // TODO: make soring inside of median
      // TODO: make better outlier removal!
      std::sort(DAB.begin(), DAB.end());
      // DAB.erase(DAB.begin(), DAB.begin() + 1);
      // DAB.erase(DAB.end() - 1, DAB.end());
      // PROCDATATYPE AvgOfDAB = DAB.template median<PROCDATATYPE>();
      PROCDATATYPE AvgOfDAB = DAB.template average<PROCDATATYPE>();

      DABHistory.addEntry(AvgOfDAB);
      DAB.clear();
    }

    FuzzyFunctionNumOfSamplesMatches.setRightLimit(
        static_cast<INDATATYPE>(SampleHistory.numberOfEntries()));
    FuzzyFunctionNumOfSamplesMismatches.setRightLimit(
        static_cast<INDATATYPE>(SampleHistory.numberOfEntries()));

    checkSignalStability();

    SignalStateInfo.ConfidenceOfMatchingState = TempConfidenceMatching;
    SignalStateInfo.ConfidenceOfMismatchingState = TempConfidenceMismatching;

    return SignalStateInfo;
  }

  /// Gives the confidence how likely the new sample matches the signal state.
  ///
  /// \param Sample is the actual sample of the observed signal.
  ///
  /// \return the confidence of the new sample is matching the signal state.
  CONFDATATYPE
  confidenceSampleMatchesSignalState(INDATATYPE Sample) noexcept {

    CONFDATATYPE ConfidenceOfBestCase = 0;

    DynamicLengthHistory<PROCDATATYPE, HistoryPolicy::FIFO>
        RelativeDistanceHistory(SampleHistory.maxLength());

    // Calculate distances to all history samples.
    for (auto &HistorySample : SampleHistory) {
      PROCDATATYPE RelativeDistance =
          relativeDistance<INDATATYPE, PROCDATATYPE>(Sample, HistorySample);
      RelativeDistanceHistory.addEntry(RelativeDistance);
    }

    // Sort all calculated distances so that the lowest distance (will get the
    // highest confidence) is at the beginning.
    RelativeDistanceHistory.sortAscending();

    CONFDATATYPE ConfidenceOfWorstFittingSample = 1;

    // Case 1 means that one (the best fitting) sample of the history is
    // compared with the new sample. Case 2 means the two best history samples
    // are compared with the new sample. And so on.
    // TODO (future): to accelerate . don't start with 1 start with some higher
    // number because a low number (i guess lower than 5) will definetely lead
    // to a low confidence. except the history is not full.

    // Case 1 means that one (the best fitting) sample of the history is
    // compared with the new sample. Case 2 means the two best history samples
    // are compared with the new sample. And so on.
    for (uint32_t Case = 0; Case < RelativeDistanceHistory.numberOfEntries();
         Case++) {

      CONFDATATYPE ConfidenceFromRelativeDistance;

      if (std::isinf(RelativeDistanceHistory[Case])) {
        // TODO (future): if fuzzy is defined in a way that infinity is not 0 it
        // would be a problem.
        ConfidenceFromRelativeDistance = 0;
      } else {
        ConfidenceFromRelativeDistance =
            FuzzyFunctionSampleMatches(RelativeDistanceHistory[Case]);
      }
      ConfidenceOfWorstFittingSample = fuzzyAND(ConfidenceOfWorstFittingSample,
                                                ConfidenceFromRelativeDistance);

      ConfidenceOfBestCase =
          fuzzyOR(ConfidenceOfBestCase,
                  fuzzyAND(ConfidenceOfWorstFittingSample,
                           FuzzyFunctionNumOfSamplesMatches(
                               static_cast<CONFDATATYPE>(Case) + 1)));
    }

    TempConfidenceMatching = ConfidenceOfBestCase;

    return ConfidenceOfBestCase;
  }

  /// Gives the confidence how likely the new sample mismatches the signal
  /// state.
  ///
  /// \param Sample is the actual sample of the observed signal.
  ///
  /// \return the confidence of the new sample is mismatching the signal state.
  CONFDATATYPE
  confidenceSampleMismatchesSignalState(INDATATYPE Sample) noexcept {

    float ConfidenceOfWorstCase = 1;

    DynamicLengthHistory<PROCDATATYPE, HistoryPolicy::FIFO>
        RelativeDistanceHistory(SampleHistory.maxLength());

    // Calculate distances to all history samples.
    for (auto &HistorySample : SampleHistory) {
      RelativeDistanceHistory.addEntry(
          relativeDistance<INDATATYPE, PROCDATATYPE>(Sample, HistorySample));
    }

    // Sort all calculated distances so that the highest distance (will get the
    // lowest confidence) is at the beginning.
    RelativeDistanceHistory.sortDescending();

    CONFDATATYPE ConfidenceOfBestFittingSample = 0;

    // TODO (future): to accelerate -> don't go until end. Confidences will only
    // get higher. See comment in "CONFDATATYPE
    // confidenceSampleMatchesSignalState(INDATATYPE Sample)".

    // Case 1 means that one (the worst fitting) sample of the history is
    // compared with the new sample. Case 2 means the two worst history samples
    // are compared with the new sample. And so on.
    for (uint32_t Case = 0; Case < RelativeDistanceHistory.numberOfEntries();
         Case++) {

      CONFDATATYPE ConfidenceFromRelativeDistance;

      if (std::isinf(RelativeDistanceHistory[Case])) {
        ConfidenceFromRelativeDistance = 1;
      } else {
        ConfidenceFromRelativeDistance =
            FuzzyFunctionSampleMismatches(RelativeDistanceHistory[Case]);
      }

      ConfidenceOfBestFittingSample = fuzzyOR(ConfidenceOfBestFittingSample,
                                              ConfidenceFromRelativeDistance);

      ConfidenceOfWorstCase =
          fuzzyAND(ConfidenceOfWorstCase,
                   fuzzyOR(ConfidenceOfBestFittingSample,
                           FuzzyFunctionNumOfSamplesMismatches(
                               static_cast<CONFDATATYPE>(Case) + 1)));
    }

    TempConfidenceMismatching = ConfidenceOfWorstCase;

    return ConfidenceOfWorstCase;
  }

  /// Gives information about the current signal state.
  ///
  /// \return a struct SignalStateInformation that contains information about
  /// the current signal state.
  SignalStateInformation<CONFDATATYPE> signalStateInformation(void) noexcept {
    return SignalStateInfo;
  }

private:
  void validateSignalState(INDATATYPE Sample) {
    // TODO (future): WorstConfidenceDistance and BestConfidenceDistance could
    // be set already in "CONFDATATYPE
    // confidenceSampleMatchesSignalState(INDATATYPE Sample)" and "CONFDATATYPE
    // confidenceSampleMismatchesSignalState(INDATATYPE Sample)" when the new
    // sample is compared to all history samples. This would save a lot time
    // because the comparisons are done only once. However, it has to be asured
    // that the these two functions are called before the insertation, and the
    // FuzzyFunctions for validation and matching have to be the same!
    CONFDATATYPE LowestConfidenceMatching = 1;
    CONFDATATYPE HighestConfidenceMismatching = 0;

    for (auto &HistorySample : SampleHistory) {
      // TODO (future): think about using different fuzzy functions for
      // validation and matching.
      LowestConfidenceMatching = fuzzyAND(
          LowestConfidenceMatching,
          FuzzyFunctionSampleMatches(relativeDistance<INDATATYPE, PROCDATATYPE>(
              Sample, HistorySample)));

      HighestConfidenceMismatching =
          fuzzyOR(HighestConfidenceMismatching,
                  FuzzyFunctionSampleMismatches(
                      relativeDistance<INDATATYPE, PROCDATATYPE>(
                          Sample, HistorySample)));
    }
    LowestConfidenceMatchingHistory.addEntry(LowestConfidenceMatching);
    HighestConfidenceMismatchingHistory.addEntry(HighestConfidenceMismatching);

    LowestConfidenceMatching = LowestConfidenceMatchingHistory.lowestEntry();
    HighestConfidenceMismatching =
        HighestConfidenceMismatchingHistory.highestEntry();

    SignalStateInfo.ConfidenceStateIsValid =
        fuzzyAND(LowestConfidenceMatching,
                 FuzzyFunctionNumOfSamplesValid(static_cast<INDATATYPE>(
                     SignalStateInfo.NumberOfInsertedSamplesAfterEntrance)));

    SignalStateInfo.ConfidenceStateIsInvalid =
        fuzzyOR(HighestConfidenceMismatching,
                FuzzyFunctionNumOfSamplesInvalid(static_cast<INDATATYPE>(
                    SignalStateInfo.NumberOfInsertedSamplesAfterEntrance)));

    if (SignalStateInfo.ConfidenceStateIsValid >
        SignalStateInfo.ConfidenceStateIsInvalid) {

      if (SignalStateInfo.StateIsValid) {
        SignalStateInfo.StateJustGotValid = false;
      } else {
        SignalStateInfo.StateJustGotValid = true;
      }

      SignalStateInfo.StateIsValid = true;
      SignalStateInfo.StateIsValidAfterReentrance = true;
    }
  }

  void checkSignalStability(void) {

    /*
  std::cout << "LookbackTest: " << std::endl;
  for (unsigned int t = 1; t <= DriftLookbackRange + 5; t++) {
    std::cout << "t=" << t
              << " -> c=" << FuzzyFunctionSignalConditionLookBack(t)
              << std::endl;

    //(*FuzzyFunctionTimeSystemFunctioning)(
    // static_cast<INDATATYPE>(TimeOfDisparity));
  }
  getchar();
*/
    SignalStateInfo.ConfidenceStateIsStable = 0;
    SignalStateInfo.ConfidenceStateIsDrifting = 0;

    /*
    std::cout << "ConfidenceStateIsStable (before): "
              << SignalStateInfo.ConfidenceStateIsStable << std::endl;
    std::cout << "ConfidenceStateIsDrifting (before): "
              << SignalStateInfo.ConfidenceStateIsDrifting << std::endl;
*/

    if (DABHistory.numberOfEntries() >= 2) {
      /*
            // EXPERIMENTING
            for (unsigned int t = 1;
                 t <= DriftLookbackRange && t < DABHistory.numberOfEntries();
         t++) {

              // AND

              SignalStateInfo.ConfidenceStateIsStable = fuzzyOR(
                  SignalStateInfo.ConfidenceStateIsStable,
                  fuzzyAND(
                      FuzzyFunctionSignalIsStable(
                          relativeDistance<INDATATYPE, PROCDATATYPE>(
                              DABHistory[DABHistory.numberOfEntries() - 1],
                              DABHistory[DABHistory.numberOfEntries() - (t +
         1)])), FuzzyFunctionSignalConditionLookBack(t)));

              SignalStateInfo.ConfidenceStateIsDrifting = fuzzyOR(
                  SignalStateInfo.ConfidenceStateIsDrifting,
                  fuzzyAND(
                      FuzzyFunctionSignalIsDrifting(
                          relativeDistance<INDATATYPE, PROCDATATYPE>(
                              DABHistory[DABHistory.numberOfEntries() - 1],
                              DABHistory[DABHistory.numberOfEntries() - (t +
         1)])), FuzzyFunctionSignalConditionLookBack(t))); */

      /*
              std::cout
                  << "t=" << t
                  << ", DABact=" << DABHistory[DABHistory.numberOfEntries() -
         1]
                  << ", DAB_t-" << t << "="
                  << DABHistory[DABHistory.numberOfEntries() - (t + 1)]
                  << " / FuzzyStb="
                  << FuzzyFunctionSignalIsStable(
                         relativeDistance<INDATATYPE, PROCDATATYPE>(
                             DABHistory[DABHistory.numberOfEntries() - 1],
                             DABHistory[DABHistory.numberOfEntries() - (t +
         1)]))
                  << ", FuzzyDft="
                  << FuzzyFunctionSignalIsDrifting(
                         relativeDistance<INDATATYPE, PROCDATATYPE>(
                             DABHistory[DABHistory.numberOfEntries() - 1],
                             DABHistory[DABHistory.numberOfEntries() - (t +
         1)]))
                  << ", FuzzyLB=" << FuzzyFunctionSignalConditionLookBack(t)
                  << std::endl;
      */
      // MULTI
      /*
              SignalStateInfo.ConfidenceStateIsStable = fuzzyOR(
                  SignalStateInfo.ConfidenceStateIsStable,
                  FuzzyFunctionSignalIsStable(
                      relativeDistance<INDATATYPE, PROCDATATYPE>(
                          DABHistory[DABHistory.numberOfEntries() - 1],
                          DABHistory[DABHistory.numberOfEntries() - (t + 1)]))
         * FuzzyFunctionSignalConditionLookBack(t));

              SignalStateInfo.ConfidenceStateIsDrifting = fuzzyOR(
                  SignalStateInfo.ConfidenceStateIsDrifting,
                  FuzzyFunctionSignalIsDrifting(
                      relativeDistance<INDATATYPE, PROCDATATYPE>(
                          DABHistory[DABHistory.numberOfEntries() - 1],
                          DABHistory[DABHistory.numberOfEntries() - (t + 1)]))
         * FuzzyFunctionSignalConditionLookBack(t));
      */
      // std::cout << "t = " << t << ", HistLength = " <<
      // DABHistory.numberOfEntries() << std::endl;
      //}

      // EXPERIMENTING -> following outcommented block was the published code

      SignalStateInfo.ConfidenceStateIsStable = FuzzyFunctionSignalIsStable(
          relativeDistance<INDATATYPE, PROCDATATYPE>(
              DABHistory[DABHistory.numberOfEntries() - 1], DABHistory[0]));
      SignalStateInfo.ConfidenceStateIsDrifting = FuzzyFunctionSignalIsDrifting(
          relativeDistance<INDATATYPE, PROCDATATYPE>(
              DABHistory[DABHistory.numberOfEntries() - 1], DABHistory[0]));
    }

    /*
    std::cout << "ConfidenceStateIsStable (after): "
              << SignalStateInfo.ConfidenceStateIsStable << std::endl;
    std::cout << "ConfidenceStateIsDrifting (after): "
              << SignalStateInfo.ConfidenceStateIsDrifting << std::endl;
*/

    /*
    else {
      // Initializing the following variables because (at this moment) we do not
      // know if the signal is stable or drifting.
      SignalStateInfo.ConfidenceStateIsStable = 0;
      SignalStateInfo.ConfidenceStateIsDrifting = 0;
    }
    */

    if (SignalStateInfo.ConfidenceStateIsStable >
        SignalStateInfo.ConfidenceStateIsDrifting) {
      SignalStateInfo.StateCondition = StateConditions::STABLE;
    } else if (SignalStateInfo.ConfidenceStateIsStable <
               SignalStateInfo.ConfidenceStateIsDrifting) {
      SignalStateInfo.StateCondition = StateConditions::DRIFTING;
    } else {
      SignalStateInfo.StateCondition = StateConditions::UNKNOWN;
      /*
            if (SignalStateInfo.ConfidenceStateIsStable != 0)
              getchar();
              */
    }
  }
};

} // End namespace agent
} // End namespace rosa

#endif // ROSA_AGENT_SIGNALSTATE_HPP
