//===-- rosa/agent/Reliability.h --------------------------------*- C++ -*-===//
//
//                                 The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/Reliability.h
///
/// \author Daniel Schnoell (danielschnoell@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Declaration of `rosa::Reliability` base-class.
///
//===----------------------------------------------------------------------===//

#ifndef ROSA_AGENT_RELIABILITY_H
#define ROSA_AGENT_RELIABILITY_H

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

#include <vector>

namespace rosa {
namespace agent {

template <typename Type> class Reliability_LowLevel : public Functionality {
  struct PossibleScore {
    int score;
    Type confOrRel;
  };
  std::vector<Type> Sensor_History;
  std::vector<PossibleScore *> actualPossibleScores;
  vector<PossibleScore *> *suggestedScores;
  Abstraction<typedef, Type> *reliabilityAbsolute;
  Abstraction<typedef, Type> *reliabilitySlope;
  Confidence<Type>

  long ValueSetCounter = 1; // I have no idea what this is

  Type getRelibility(Type actualValue, Type lastValue,
                     unsigned int valueSetCounter) {

    Type relAbs, relSlo;

    relAbs = reliabilityAbsolute(actualValue);
    relSlo =
        reliabilitySlope((lastValue - actualValue) / (Type)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;
    rel = std::min(relAbs, relSlo);

    return rel;
  }

  Type getInputReliability(Type actualSensorValue) {

    Type inputReliability;
    Type previousSensorValue;

    if (Sensor_History.size() > 0) {
      inputReliability = reliabilityModule->getRelibility(
          actualSensorValue, Sensor_History.at(0), ValueSetCounter);
    } else {
      inputReliability =
          reliabilityModule->getAbsoluteReliability(actualSensorValue);
    }
    return inputReliability;
  }

  void triggerLowLevelAgent(Type actualSensorValue) {

    // calculate input reliability
    inputReliability = getInputReliability(actualSensorValue);

    // combine input reliability and confidence for all actual possible
    // scores
    combineConfidenceAndReliabilityOfActualPossibleScores(actualPossibleScores,
                                                          inputReliability);

    // printf("#actualPossibleScores (after combi): %u\n",
    // actualPossibleScores->size());

    // FROM HERE - MASTER INPUT
    // clear suggested scores from last cycle
    masterAgentHandlerOfAgent->clearSuggestedScores();
    masterAgentHandlerOfAgent->read_masterAgentValues();

    vector<PossibleScore *> *suggestedScores =
        masterAgentHandlerOfAgent->getSuggestedScores();

    // if (id == 6)
    // printf("++++++++\nsuggestedScores: %u\nactualPossibleScores:
    // %u\n+++++++++\n", suggestedScores->size(),
    // actualPossibleScores->size());

    for (auto &sS : *suggestedScores) {
      for (auto &aPs : *actualPossibleScores) {
        if (aPs->score == sS->score) {
          // combine suggested score with actual score
          // NOTE: multiplication, AND, or average would be alternatives
          // (best to worst: AND > multiplication >> average) aPs->confOrRel
          // = std::min(aPs->confOrRel, sS->confOrRel);
          aPs->confOrRel = (aPs->confOrRel + sS->confOrRel);
          // aPs->confOrRel = aPs->confOrRel * sS->confOrRel;
        }
      }
    }
    // TO HERE - MASTER INPUT

    // if (id == 6)
    // printf("++++++++\nsuggestedScores: %u\nactualPossibleScores:
    // %u\n+++++++++\n", suggestedScores->size(),
    // actualPossibleScores->size());

    // save actual possible scores in history
    saveActualPossibleScoresInHistory(actualPossibleScores);

    // evaluate all possible scores (based on the history)
    vector<PossibleScore *> possibleScores;

    // printf("SIZE BEFORE: %u\n", possibleScores.size());

    getAllPossibleScoresBasedOnHistory(&possibleScores);

    // printf("SIZE AFTER: %u\n", possibleScores.size());

    // sort all possible scores so that outputRel_i >= outputRel_i+1
    std::sort(possibleScores.begin(), possibleScores.end(), compareByConfRel);

    // send possible scores to high level agent
    // sendPossibleScoresToMaster(&possibleScores);

    for (auto &pS : possibleScores) {
#ifdef PRINT
      printf("   > possible Score %i / %f\n", pS->score, pS->confOrRel);
#endif // PRINT
    }

    // send most likely score to high level agent
    sendMostLikelyScoreToMaster(&possibleScores);
  }
}

}; // namespace agent

} // namespace rosa
} // namespace rosa

#endif // !ROSA_AGENT_RELIABILITY_H
