//===- 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 \c rosa::Agent instances using
/// Relianility Functionalities.
///
//===----------------------------------------------------------------------===//

#define Reliability_trace_level 5

#include "rosa/config/version.h"

#include "rosa/core/Agent.hpp"
#include "rosa/core/MessagingSystem.hpp"

#include "rosa/support/log.h"
#include "rosa/support/terminal_colors.h"

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

#include <vector>
#include <map>

using namespace rosa::agent;

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

	// defining Range confidence



	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/9));


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


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

	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 ---------------------------------------------------------------- */ 

  for ( int a= 0 ; a <10 ; a++)
    std::cout <<"a: " << a << "\n" << (lowlevel->feedback({{0,0},{1,0.3},{2,0.5}}),lowlevel->operator()(a)) << "\n";

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

  std::cout << "------------------------------------------------------------------------------------------------\n";
  std::cout << "------------------------------------High level Test---------------------------------------------\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>());

  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>());

  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()(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;

}