#ifndef STATEHANDLERUTILS_H
#define STATEHANDLERUTILS_H
#include "rosa/agent/Abstraction.hpp"
#include "rosa/agent/Confidence.hpp"
#include "rosa/agent/FunctionAbstractions.hpp"
#include <functional>
#include <iostream>
#include <tuple>
#include <vector>

#include "rosa/config/version.h"

#include "rosa/agent/SignalStateDetector.hpp"
#include "rosa/agent/SystemStateDetector.hpp"
#include "rosa/deluxe/DeluxeContext.hpp"

#include "rosa/support/csv/CSVReader.hpp"
#include "rosa/support/csv/CSVWriter.hpp"

#include <fstream>
#include <limits>
#include <memory>
#include <streambuf>
using namespace rosa;
using namespace rosa::agent;
using namespace rosa::deluxe;
using namespace rosa::terminal;

// Signal State

using SignalStateTuple =
    DeluxeTuple<float, uint32_t, uint8_t, float, float, float, float, float,
                float, uint8_t, uint32_t, bool, bool, bool>;

AgentHandle createSignalStateDetectorAgent(
    std::unique_ptr<DeluxeContext> &C, const std::string &Name,
    std::shared_ptr<
        SignalStateDetector<float, float, float, HistoryPolicy::FIFO>>
        SigSD) {

  using Input = std::pair<DeluxeTuple<float>, bool>;

  using Result = Optional<SignalStateTuple>;
  using Handler = std::function<Result(Input)>;

  return C->createAgent(
      Name, Handler([&Name, &SigSD](Input I) -> Result {
        LOG_INFO_STREAM << "\n******\n"
                        << Name << " " << (I.second ? "<New>" : "<Old>")
                        << " value: " << std::get<0>(I.first) << "\n******\n";
        auto StateInfo = SigSD->detectSignalState(std::get<0>(I.first));

        if (I.second) {
          SignalStateTuple Res = {
              std::get<0>(I.first), StateInfo.StateID, StateInfo.SignalProperty,
              //@benedikt: I changed this
              // StateInfo.SignalStateConfidence,
              StateInfo.ConfidenceOfMatchingState,
              StateInfo.ConfidenceOfMismatchingState,
              StateInfo.ConfidenceStateIsValid,
              StateInfo.ConfidenceStateIsInvalid,
              StateInfo.ConfidenceStateIsStable,
              StateInfo.ConfidenceStateIsDrifting, StateInfo.StateCondition,
              StateInfo.NumberOfInsertedSamplesAfterEntrance,
              StateInfo.StateIsValid, StateInfo.StateJustGotValid,
              StateInfo.StateIsValidAfterReentrance};
          return Result(Res);
        }
        return Result();
      }));
}

// System State

using SystemStateTuple = DeluxeTuple<std::string, uint32_t, float, uint8_t,
                                     uint32_t, bool, bool, bool, bool>;

template <std::size_t size, typename ret, typename functype, typename... A>
struct Handler_helper;

template <typename B, typename func, typename A, typename... As>
struct function_helper {
  static_assert(std::conjunction_v<std::is_same<A, As>...>,
                "All types need to be identical");

  static B function(A valA, As... valAs) {
    std::vector<A> ar({valA, valAs...});
    return func()(ar);
  }
};

template <typename ret, typename typeA, typename functype, typename... B>
struct Handler_helper<0, ret, functype, typeA, B...> {
  using handler = function_helper<ret, functype, B...>;
};

template <std::size_t size, typename ret, typename typeA, typename functype,
          typename... B>
struct Handler_helper<size, ret, functype, typeA, B...> {
  using handler =
      typename Handler_helper<size - 1, ret, functype, typeA,
                              std::pair<typeA, bool>, B...>::handler;
};

template <std::size_t size, typename ret, typename functype, typename typeA>
using Handler = typename Handler_helper<size, ret, functype, typeA>::handler;

// todo: state-detector durschleifen
template <typename ret, typename A> struct function {
  ret operator()(A a) {
    // std::vector<SystemStateInformation> out;
    for (auto tmp1 : a) {
      // convert tuple to info struct out.push_back({});
      (void)tmp1;
      LOG_INFO_STREAM << "new SignalStateTuple!\n";
    }
    // feed state detector
    // return result
    return ret();
  }
};

using arr = std::vector<std::pair<SignalStateTuple, bool>>;

template <size_t NumOfSlaves>
AgentHandle createSystemStateDetectorAgent(
    std::unique_ptr<DeluxeContext> &C, const std::string &Name,
    std::shared_ptr<PartialFunction<uint32_t, float>> BrokenDelayFunction,
    std::shared_ptr<PartialFunction<uint32_t, float>> OkDelayFunction) {
  LOG_TRACE("Creating fixed SystemStateDetectorAgent");
  using Input = SignalStateTuple;
  using Result = Optional<SystemStateTuple>;

  auto HandlerFunction =
      Handler<NumOfSlaves, Result, function<Optional<SystemStateTuple>, arr>,
              Input>::function;
  std::shared_ptr<
      SystemStateDetector<uint32_t, float, float, HistoryPolicy::FIFO>>
      //@benedikt: I had to insert a dummy number for NumberOfSignals (I
      // inserted 5)
      SysSD(
          new SystemStateDetector<uint32_t, float, float, HistoryPolicy::FIFO>(
              std::numeric_limits<uint32_t>::max(), 5, BrokenDelayFunction,
              OkDelayFunction));

  return C->createAgent(Name, std::function(HandlerFunction));
}

AgentHandle createSystemStateDetectorAgent(
    std::unique_ptr<DeluxeContext> &C, const std::string &Name,
    size_t NumOfSlaves,
    std::shared_ptr<PartialFunction<uint32_t, float>> BrokenDelayFunction,
    std::shared_ptr<PartialFunction<uint32_t, float>> OkDelayFunction) {
  LOG_TRACE("Creating dynamic SystemStateDetectorAgent");
  switch (NumOfSlaves) {
  // clang-format off
  case  2: return createSystemStateDetectorAgent< 2>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case  3: return createSystemStateDetectorAgent< 3>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case  4: return createSystemStateDetectorAgent< 4>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case  5: return createSystemStateDetectorAgent< 5>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case  6: return createSystemStateDetectorAgent< 6>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case  7: return createSystemStateDetectorAgent< 7>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case  8: return createSystemStateDetectorAgent< 8>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case  9: return createSystemStateDetectorAgent< 9>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 10: return createSystemStateDetectorAgent<10>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 11: return createSystemStateDetectorAgent<11>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 12: return createSystemStateDetectorAgent<12>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 13: return createSystemStateDetectorAgent<13>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 14: return createSystemStateDetectorAgent<14>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 15: return createSystemStateDetectorAgent<15>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 16: return createSystemStateDetectorAgent<16>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 17: return createSystemStateDetectorAgent<17>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 18: return createSystemStateDetectorAgent<18>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 19: return createSystemStateDetectorAgent<19>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 20: return createSystemStateDetectorAgent<20>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 21: return createSystemStateDetectorAgent<21>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 22: return createSystemStateDetectorAgent<22>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 23: return createSystemStateDetectorAgent<23>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 24: return createSystemStateDetectorAgent<24>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 25: return createSystemStateDetectorAgent<25>(C, Name, BrokenDelayFunction, OkDelayFunction);
  case 1:
  default: return createSystemStateDetectorAgent<1>(C, Name, BrokenDelayFunction, OkDelayFunction);
    // clang-format on
  }
}
#endif // STATEHANDLERUTILS_H
