#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 "rosa/app/Application.hpp"

#include <map>
#include <vector>

using namespace rosa::agent;
using namespace rosa;
using namespace rosa::app;
using namespace rosa::terminal;


auto create_lowlevel_func() {
  std::unique_ptr<RangeConfidence<SensorValueType, StateType, ReliabilityType>>
      Confidence(new RangeConfidence<SensorValueType, StateType,
                                     ReliabilityType>(
          {{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 ReliabilityForLowLevelAgents<SensorValueType, StateType,
                                                   ReliabilityType>();

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

  lowlevel->setConfidenceFunction(Confidence);
  lowlevel->setAbsoluteReliabilityFunction(Reliability);
  lowlevel->setReliabilitySlopeFunction(ReliabilitySlope);
  lowlevel->setTimeFunctionForLikelinessFunction(TimeConfidence);
  lowlevel->setIdentifiers(states);
  lowlevel->setHistoryLength(2);
  lowlevel->setTimeStep(1);

  return lowlevel;
}

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

  ReliabilityForHighLevelAgents<StateType, ReliabilityType> *highlevel =
      new ReliabilityForHighLevelAgents<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));
  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));
  std::unique_ptr<Abstraction<long, double>> func3(
      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->addCrossLikelinessProfile(0, 1, func1);
  CrossReliability1->addCrossLikelinessProfile(0, 2, func2);
  CrossReliability1->addCrossLikelinessProfile(2, 1, func3);
  CrossReliability1->setCrossReliabilityMethod(
      CrossReliability<StateType, ReliabilityType>::AVERAGE);
  CrossReliability1->setCrossLikelinessParameter(1);

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

  std::unique_ptr<Abstraction<long, double>> func4(
      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));
  std::unique_ptr<Abstraction<long, double>> func5(
      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));
  std::unique_ptr<Abstraction<long, double>> func6(
      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->addCrossLikelinessProfile(0, 1, func4);
  CrossConfidence1->addCrossLikelinessProfile(0, 2, func5);
  CrossConfidence1->addCrossLikelinessProfile(2, 1, func6);
  CrossConfidence1->setCrossReliabilityMethod(
      CrossConfidence<StateType, ReliabilityType>::AVERAGE);
  CrossConfidence1->setCrossLikelinessParameter(1);

  highlevel->setCrossConfidence(CrossConfidence1);
  highlevel->setCrossReliability(CrossReliability1);

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

  return highlevel;
}
