//===- 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 inside a
/// master slave context.
/// \note This is not finished
///
//===----------------------------------------------------------------------===//

#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 "rosa/deluxe/DeluxeContext.hpp"

#include <map>
#include <vector>

typedef double SensorValueType;
typedef long StateType;
typedef double ReliabilityType;

#include "./helper.h" // just stuff from Rel-func to increase readability

using namespace rosa::agent;
using namespace rosa;
using namespace rosa::deluxe;
using namespace rosa::terminal;

// -------------------------------------------------------------------------------
//				Bunch of recusive templates to simplify usage
// -------------------------------------------------------------------------------

template <std::size_t, typename... types> struct conversion;

template <std::size_t size, typename... TypesA, template <typename...> class A,
          typename... TypesB, template <typename...> class B>
struct conversion<size, A<TypesA...>, B<TypesB...>> {
  using type = typename conversion<size - 1, std::tuple<TypesA...>,
                                   std::tuple<TypesB..., TypesA...>>::type;
};

template <typename... TypesA, template <typename...> class A,
          typename... TypesB, template <typename...> class B>
struct conversion<0, A<TypesA...>, B<TypesB...>> {
  using type = DeluxeTuple<TypesB...>;
};

template <std::size_t size, typename... Types>
using unrolled_data_type =
    typename conversion<size, std::tuple<Types...>, std::tuple<>>::type;

//--------------------------------------------------------------------------------
template <std::size_t max, std::size_t at> struct __convert_to_vector {
  void operator()(std::vector<ConfOrRel<StateType, ReliabilityType>> &feedback,
                  unrolled_data_type<max / 2, StateType, ReliabilityType> I) {
    __convert_to_vector<max, at - 2>()(feedback, I);
    feedback.push_back({std::get<at>(I), std::get<at + 1>(I)});
  }
};
template <std::size_t max> struct __convert_to_vector<max, 0> {
  void operator()(std::vector<ConfOrRel<StateType, ReliabilityType>> &feedback,
                  unrolled_data_type<max / 2, StateType, ReliabilityType> I) {
    feedback.push_back({std::get<0>(I), std::get<1>(I)});
  }
};

template <std::size_t number>
void convert_to_vector(
    std::vector<ConfOrRel<StateType, ReliabilityType>> &feedback,
    unrolled_data_type<number, StateType, ReliabilityType> I) {
  __convert_to_vector<number * 2, number * 2 - 2>()(feedback, I);
}
//----------------------------------------------------------------------------
// template <std::size_t size, typename... types> struct unrole_vector {};
//
// template <std::size_t size, template <typename...> v, typename... vT,
//          template <typename...> class T, typename... TT, typename... types>
// struct unrole_vector<size, v<vT...>,T<TT...>, types...> {
//  void operator()(c vec, types... A) {
//    unrole_vector<size - 1, v<vT...>, T<TT...>, auto>()(vec,
//    vec.at(size).score,
//                                                        vec.at(size).score);
//  }
//};

// ---------------------------------------------------------------------------
//					main
// ---------------------------------------------------------------------------
int main(void) {

  const std::size_t number_of_states = 3;

  std::unique_ptr<DeluxeContext> C = DeluxeContext::create("Deluxe");

  //---------------------- Sensors -------------------------------------
  //--------------------------------------------------------------------
  const std::string SensorName1 = "Sensor1";
  const std::string SensorName2 = "Sensor2";
  const std::string SensorName3 = "Sensor3";
  AgentHandle Sensor1 = C->createSensor<uint32_t, SensorValueType>(
      SensorName1, [&SensorName1](std::pair<uint32_t, bool> I) {
        LOG_INFO_STREAM << "\n******\n"
                        << SensorName1 << " master-input "
                        << (I.second ? "<New>" : "<Old>")
                        << " value: " << I.first << "\n******\n";
      });
  AgentHandle Sensor2 = C->createSensor<uint32_t, SensorValueType>(
      SensorName2, [&SensorName2](std::pair<uint32_t, bool> I) {
        LOG_INFO_STREAM << "\n******\n"
                        << SensorName2 << " master-input "
                        << (I.second ? "<New>" : "<Old>")
                        << " value: " << I.first << "\n******\n";
      });
  AgentHandle Sensor3 = C->createSensor<uint32_t, SensorValueType>(
      SensorName3, [&SensorName3](std::pair<uint32_t, bool> I) {
        LOG_INFO_STREAM << "\n******\n"
                        << SensorName3 << " master-input "
                        << (I.second ? "<New>" : "<Old>")
                        << " value: " << I.first << "\n******\n";
      });

  //------------------------- lowlevel agents --------------------------------
  //--------------------------------------------------------------------------

  const std::string LowLevelAgentName1 = "LowLevelAgent1";
  const std::string LowLevelAgentName2 = "LowLevelAgent2";
  const std::string LowLevelAgentName3 = "LowLevelAgent3";

  using conf = unrolled_data_type<
      number_of_states, StateType,
      ReliabilityType>; // this is the confidence expressed as one tuple it
                        // uses the format
                        // (first.state,first.rel,second.sate...)

  using LowLevelAgentMasterResult = void; // no return
  using LowLevelReturnFromMaster = std::pair<conf, bool>;
  using FloatMasterHandler =
      std::function<LowLevelAgentMasterResult(LowLevelReturnFromMaster)>;
  using FloatResult = Optional<DeluxeTuple<StateType, ReliabilityType>>;
  using FloatHandler =
      std::function<FloatResult(std::pair<DeluxeTuple<SensorValueType>, bool>)>;

  auto lowlevel1 = create_lowlevel_func();
  auto lowlevel2 = create_lowlevel_func();
  auto lowlevel3 = create_lowlevel_func();

  AgentHandle SlaveAgent = C->createAgent(
      LowLevelAgentName1,
      // Master-input handler.
      FloatMasterHandler(
          [&LowLevelAgentName1,
           lowlevel1](LowLevelReturnFromMaster I) -> LowLevelAgentMasterResult {
            LOG_INFO_STREAM << "inside: " << LowLevelAgentName1 << "feedback\n";
            if (I.second) {
              std::vector<ConfOrRel<StateType, ReliabilityType>> feedback;
              convert_to_vector<number_of_states>(feedback, I.first);
              lowlevel1->feedback(feedback);
            }
          }),
      // Slave-input handler.
      FloatHandler(
          [&LowLevelAgentName1, lowlevel1](
              std::pair<DeluxeTuple<SensorValueType>, bool> I) -> FloatResult {
            LOG_INFO_STREAM
                << "\n******\n"
                << LowLevelAgentName1 << " " << (I.second ? "<New>" : "<Old>")
                << " value: " << std::get<0>(I.first) << "\n******\n";

            auto tmp = lowlevel1->operator()(std::get<0>(I.first));
            DeluxeTuple<long, double> ret(tmp.score, tmp.Reliability);
            return {ret};
          }));

  AgentHandle SlaveAgent2 = C->createAgent(
      LowLevelAgentName2,
      // Master-input handler.
      FloatMasterHandler(
          [&LowLevelAgentName2,
           lowlevel2](LowLevelReturnFromMaster I) -> LowLevelAgentMasterResult {
            LOG_INFO_STREAM << "inside: " << LowLevelAgentName2 << "feedback\n";
            if (I.second) {
              std::vector<ConfOrRel<StateType, ReliabilityType>> feedback;
              convert_to_vector<number_of_states>(feedback, I.first);
              lowlevel2->feedback(feedback);
            }
          }),
      // Slave-input handler.
      FloatHandler(
          [&LowLevelAgentName2, lowlevel2](
              std::pair<DeluxeTuple<SensorValueType>, bool> I) -> FloatResult {
            LOG_INFO_STREAM
                << "\n******\n"
                << LowLevelAgentName2 << " " << (I.second ? "<New>" : "<Old>")
                << " value: " << std::get<0>(I.first) << "\n******\n";

            auto tmp = lowlevel2->operator()(std::get<0>(I.first));
            DeluxeTuple<long, double> ret(tmp.score, tmp.Reliability);
            return {ret};
          }));

  AgentHandle SlaveAgent3 = C->createAgent(
      LowLevelAgentName3,
      // Master-input handler.
      FloatMasterHandler(
          [&LowLevelAgentName3,
           lowlevel3](LowLevelReturnFromMaster I) -> LowLevelAgentMasterResult {
            LOG_INFO_STREAM << "inside: " << LowLevelAgentName3 << "feedback\n";
            if (I.second) {
              std::vector<ConfOrRel<StateType, ReliabilityType>> feedback;
              convert_to_vector<number_of_states>(feedback, I.first);
              lowlevel3->feedback(feedback);
            }
          }),
      // Slave-input handler.
      FloatHandler(
          [&LowLevelAgentName3, lowlevel3](
              std::pair<DeluxeTuple<SensorValueType>, bool> I) -> FloatResult {
            LOG_INFO_STREAM
                << "\n******\n"
                << LowLevelAgentName3 << " " << (I.second ? "<New>" : "<Old>")
                << " value: " << std::get<0>(I.first) << "\n******\n";

            auto tmp = lowlevel3->operator()(std::get<0>(I.first));
            DeluxeTuple<long, double> ret(tmp.score, tmp.Reliability);
            return {ret};
          }));

  //------------------------- lookup copy of rel------------------------------
  //--------------------------------------------------------------------------

  auto highlevel = create_highlevel_func();

  using conf1 = Optional<unrolled_data_type<
      number_of_states, StateType,
      ReliabilityType>>; // this is the confidence expressed as one tuple it
                         // uses the format
                         // (first.state,first.rel,second.sate...)

  using MasterResult =
      std::tuple<Optional<DeluxeTuple<ReliabilityType>>, conf1, conf1, conf1>;
  using SlaveOutputs = DeluxeTuple<StateType, ReliabilityType>;
  using MasterHandler = std::function<MasterResult(
      std::pair<SlaveOutputs, bool>, std::pair<SlaveOutputs, bool>,
      std::pair<SlaveOutputs, bool>)>;
  AgentHandle SumAgent = C->createAgent(
      "Master Agent",
      MasterHandler([&](std::pair<SlaveOutputs, bool> I0,
                        std::pair<SlaveOutputs, bool> I1,
                        std::pair<SlaveOutputs, bool> I2) -> MasterResult {
        ReliabilityForHighLevelAgents<StateType, ReliabilityType>::InputType
            input;
        input.push_back({0, std::get<0>(I0.first), std::get<1>(I0.first)});
        input.push_back({1, std::get<0>(I1.first), std::get<1>(I1.first)});
        input.push_back({2, std::get<0>(I2.first), std::get<1>(I2.first)});
        auto out = highlevel->operator()(input);

        DeluxeTuple<ReliabilityType> rel(out.CrossReliability);

        conf c[3];
        for (std::size_t at = 0; at < 3; at++) {
          std::get<0>(c[at]) = out.CrossConfidence.at(at).at(0).score;
          std::get<1>(c[at]) = out.CrossConfidence.at(at).at(0).Reliability;
          std::get<2>(c[at]) = out.CrossConfidence.at(at).at(1).score;
          std::get<3>(c[at]) = out.CrossConfidence.at(at).at(1).Reliability;
          std::get<4>(c[at]) = out.CrossConfidence.at(at).at(2).score;
          std::get<5>(c[at]) = out.CrossConfidence.at(at).at(2).Reliability;
        }
        return {{rel}, {c[0]}, {c[1]}, {c[2]}};
      }));

  // -------------------------------------------------------------------------
  //		Input data into sensors and listen to output
  // -------------------------------------------------------------------------




  delete highlevel;
  delete lowlevel1;
  delete lowlevel2;
}