//===- examples/agent-functionalities/Reliability-functionality.cpp *C++-*-===//
//
//                                 The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file examples/agent-functionalities/Reliability-functionality.cpp
///
/// \author Daniel Schnoell (daniel.schnoell@tuwien.ac.at )
///
/// \date 2019
///
/// \brief A simple example on defining Relianility Functionalities.
///
//===----------------------------------------------------------------------===//

#define Reliability_trace_level 5

#include "rosa/config/version.h"

#include "rosa/support/log.h"

#include "rosa/agent/CrossReliability.h"
#include "rosa/agent/RangeConfidence.hpp"
#include "rosa/agent/Reliability.h"

#include <map>
#include <vector>

using namespace rosa::agent;

int main(void) {
  typedef double SensorValueType;
  typedef long StateType;
  typedef double ReliabilityType;

  std::unique_ptr<RangeConfidence<ReliabilityType, StateType, SensorValueType>>
      Confidence(new RangeConfidence<ReliabilityType, StateType,
                                     SensorValueType>(
          {{0, PartialFunction<double, double>(
                   {
                       {{0, 3},
                        std::make_shared<LinearFunction<double, double>>(
                            0, 1.0 / 3)},
                       {{3, 6},
                        std::make_shared<LinearFunction<double, double>>(1, 0)},
                       {{6, 9},
                        std::make_shared<LinearFunction<double, double>>(
                            3.0, -1.0 / 3)},
                   },
                   0)},
           {1, PartialFunction<double, double>(
                   {
                       {{6, 9},
                        std::make_shared<LinearFunction<double, double>>(
                            -2, 1.0 / 3)},
                       {{9, 12},
                        std::make_shared<LinearFunction<double, double>>(1, 0)},
                       {{12, 15},
                        std::make_shared<LinearFunction<double, double>>(
                            5, -1.0 / 3)},
                   },
                   0)},
           {2, PartialFunction<double, double>(
                   {
                       {{12, 15},
                        std::make_shared<LinearFunction<double, double>>(
                            -4, 1.0 / 3)},
                       {{15, 18},
                        std::make_shared<LinearFunction<double, double>>(1, 0)},
                       {{18, 21},
                        std::make_shared<LinearFunction<double, double>>(
                            7, -1.0 / 3)},
                   },
                   0)}}));

  std::unique_ptr<Abstraction<SensorValueType, ReliabilityType>> Reliability(
      new LinearFunction<SensorValueType, ReliabilityType>(1, -1.0 / 3));

  std::unique_ptr<Abstraction<SensorValueType, ReliabilityType>>
      ReliabilitySlope(
          new LinearFunction<SensorValueType, ReliabilityType>(1, -1.0 / 3));

  std::unique_ptr<Abstraction<std::size_t, ReliabilityType>> TimeConfidence(
      new LinearFunction<std::size_t, ReliabilityType>(1, -1.0 / 3));

  auto lowlevel = new LowLevel<SensorValueType, StateType, ReliabilityType>();

  std::vector<long> states;
  states.push_back(0);
  states.push_back(1);
  states.push_back(2);

  lowlevel->setConfidenceFunction(Confidence);
  lowlevel->setReliabilityFunction(Reliability);
  lowlevel->setReliabilitySlopeFunction(ReliabilitySlope);
  lowlevel->setTimeConfidenceFunction(TimeConfidence);
  lowlevel->setStates(states);
  lowlevel->setHistoryLength(2);
  lowlevel->setValueSetCounter(1);

  /* ----------------------------- Do Something
   * ---------------------------------------------------------------- */
  std::cout << "Testing the lowlevel component with static feedback telling it "
               "that the most lickely state is 2.\n";
  for (int a = 0; a < 30; a++)
    std::cout << "a: " << a << "\n"
              << (lowlevel->feedback({{0, 0}, {1, 0.3}, {2, 0.8}}),
                  lowlevel->operator()(a))
              << "\n";

  /* ----------------------------- Cleanup
   * --------------------------------------------------------------------- */

  std::cout << "---------------------------------------------------------------"
               "---------------------------------\n";
  std::cout << "------------------------------------High level "
               "Test---------------------------------------------\n";

  std::cout
      << "Configured in a way that the Master thinks that both Sensors "
         "should have the same State.\n While feeding both the \"opposite\" "
         "values one acending the other decending from the maximum.\n";

  std::unique_ptr<RangeConfidence<ReliabilityType, StateType, SensorValueType>>
      Confidence2(new RangeConfidence<ReliabilityType, StateType,
                                      SensorValueType>(
          {{0, PartialFunction<double, double>(
                   {
                       {{0, 3},
                        std::make_shared<LinearFunction<double, double>>(
                            0, 1.0 / 3)},
                       {{3, 6},
                        std::make_shared<LinearFunction<double, double>>(1, 0)},
                       {{6, 9},
                        std::make_shared<LinearFunction<double, double>>(
                            3.0, -1.0 / 3)},
                   },
                   0)},
           {1, PartialFunction<double, double>(
                   {
                       {{6, 9},
                        std::make_shared<LinearFunction<double, double>>(
                            -2, 1.0 / 3)},
                       {{9, 12},
                        std::make_shared<LinearFunction<double, double>>(1, 0)},
                       {{12, 15},
                        std::make_shared<LinearFunction<double, double>>(
                            5, -1.0 / 3)},
                   },
                   0)},
           {2, PartialFunction<double, double>(
                   {
                       {{12, 15},
                        std::make_shared<LinearFunction<double, double>>(
                            -4, 1.0 / 3)},
                       {{15, 18},
                        std::make_shared<LinearFunction<double, double>>(1, 0)},
                       {{18, 21},
                        std::make_shared<LinearFunction<double, double>>(
                            7, -1.0 / 3)},
                   },
                   0)}}));

  std::unique_ptr<Abstraction<SensorValueType, ReliabilityType>> Reliability2(
      new LinearFunction<SensorValueType, ReliabilityType>(1, -1.0 / 9));

  std::unique_ptr<Abstraction<SensorValueType, ReliabilityType>>
      ReliabilitySlope2(
          new LinearFunction<SensorValueType, ReliabilityType>(1, -1.0 / 9));

  std::unique_ptr<Abstraction<std::size_t, ReliabilityType>> TimeConfidence2(
      new LinearFunction<std::size_t, ReliabilityType>(1, -1.0 / 9));

  auto lowlevel2 = new LowLevel<SensorValueType, StateType, ReliabilityType>();

  std::vector<long> states2;
  states2.push_back(0);
  states2.push_back(1);
  states2.push_back(2);

  lowlevel2->setConfidenceFunction(Confidence2);
  lowlevel2->setReliabilityFunction(Reliability2);
  lowlevel2->setReliabilitySlopeFunction(ReliabilitySlope2);
  lowlevel2->setTimeConfidenceFunction(TimeConfidence2);
  lowlevel2->setStates(states2);
  lowlevel2->setHistoryLength(2);
  lowlevel2->setValueSetCounter(1);

  HighLevel<StateType, ReliabilityType> *highlevel =
      new HighLevel<StateType, ReliabilityType>();

  std::unique_ptr<CrossReliability<StateType, ReliabilityType>>
      CrossReliability1(new CrossReliability<StateType, ReliabilityType>());

  std::unique_ptr<Abstraction<long, double>> func1(new PartialFunction<long, double>(
      {
          {{0, 1}, std::make_shared<LinearFunction<long, double>>(1, 0)},
          {{1, 2}, std::make_shared<LinearFunction<long, double>>(2, -1.0)},
      },
      0));

  CrossReliability1->addCrossReliabilityProfile(0, 1, func1);
  CrossReliability1->setCrossReliabilityMethod(
      CrossReliability<StateType, ReliabilityType>::AVERAGE);
  CrossReliability1->setCrossReliabilityParameter(1);

  std::unique_ptr<CrossConfidence<StateType, ReliabilityType>> CrossConfidence1(
      new CrossConfidence<StateType, ReliabilityType>());

  std::unique_ptr<Abstraction<long, double>> func2(new PartialFunction<long, double>(
      {
          {{0, 1}, std::make_shared<LinearFunction<long, double>>(1, 0)},
          {{1, 2}, std::make_shared<LinearFunction<long, double>>(2, -1.0)},
      },
      0));

  CrossConfidence1->addCrossReliabilityProfile(0, 1, func2);
  CrossConfidence1->setCrossReliabilityMethod(
      CrossConfidence<StateType, ReliabilityType>::AVERAGE);
  CrossConfidence1->setCrossReliabilityParameter(1);

  highlevel->setFunction(CrossConfidence1);
  highlevel->setFunction(CrossReliability1);

  highlevel->addStates(0, states);
  highlevel->addStates(1, states);

  for (int a = 0; a < 21; a++) {
    auto out1 = lowlevel->operator()(a), out2 = lowlevel2->operator()((int)21 - a);
    std::cout << "s1: " << out1 << "\ns2:" << out2 << "\n";
    std::vector<std::tuple<rosa::id_t, StateType, ReliabilityType>> tmp2;
    tmp2.push_back({0, out1.score, out1.Reliability});
    tmp2.push_back({1, out2.score, out2.Reliability});

    auto out_o = highlevel->operator()(tmp2);
    std::cout << "it: " << a << "\t rel: " << out_o.CrossReliability << "\n";
    std::cout << "\t subs:\n";
    for (auto q : out_o.CrossConfidence) {
      std::cout << "\t\t id:" << q.first << "\n";
      /*
      for(auto z: q.second)
      {
        std::cout << "\t\t\t score: " << z.score << "\tRel: " << z.Reliability
      << "\n"; tmp.push_back({z.score,z.Reliability});
      }
      */
      for (auto z : q.second) {
        std::cout << "\t\t\t score: " << z.score << "\tRel: " << z.Reliability
                  << "\n";
      }

      if (q.first == 0)
        lowlevel->feedback(q.second);
      else
        lowlevel2->feedback(q.second);
    }
  }

  delete lowlevel;
  delete lowlevel2;
}