//===-- examples/messaging/messaging.cpp ------------------------*- C++ -*-===//
//
//                                 The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file examples/messaging/messaging.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief An example showcasing features related to the \c rosa::Message class.
///
//===----------------------------------------------------------------------===//

#include "rosa/config/version.h"

#include "rosa/core/MessageHandler.hpp"

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

using namespace rosa;
using namespace rosa::terminal;

int main(void) {
  LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "messaging"
                  << Color::Default << '\n';

  auto &Log = LOG_INFO_STREAM << '\n';

  // Message interface.
  auto PMsg = Message::create<uint8_t, uint16_t>(1, 2);
  auto &Msg = *PMsg;
  Log << "Checking on a 'Message with TypeList<<uint8_t, uint16_t>>':"
      << "\n  Size: " << Msg.Size
      << "\n  Pos 0 is uint8_t: " << Msg.isTypeAt<uint8_t>(0)
      << "\n  Pos 1 is uint16_t: " << Msg.isTypeAt<uint16_t>(1)
      << "\n  Pos 2 is uint32_t: " << Msg.isTypeAt<uint32_t>(1)
      << "\n  Value at pos 0: " << PRINTABLE(Msg.valueAt<uint8_t>(0))
      << "\n  Value at pos 1: " << Msg.valueAt<uint16_t>(1) << "\n\n";

  // MessageMatcher.
  using MyMatcher = MsgMatcher<uint8_t, uint16_t>;
  Log << "Matching against 'TypeList<uint8_t, uint16_t>':"
      << "\n  matching: " << MyMatcher::doesStronglyMatch(Msg);
  auto Vs = MyMatcher::extractedValues(Msg);
  Log << "\n  value: '(" << PRINTABLE(std::get<0>(Vs)) << ", "
      << std::get<1>(Vs) << ")'\n\n";

  using MyWrongMatcher = MsgMatcher<uint8_t, uint8_t>;
  Log << "Matching against 'TypeList<uint8_t, uint8_t>':"
      << "\n  matching: " << MyWrongMatcher::doesStronglyMatch(Msg) << "\n\n";

  using MyAtom = AtomConstant<atom("atom")>;
  const MyAtom &A = MyAtom::Value;
  using MyNAtom = AtomConstant<atom("noatom")>;
  auto PAMsg = Message::create(A);
  auto &AMsg = *PAMsg;
  Log << "Checking on a 'Message with TypeList<AtomConstant<atom(\"atom\")>>':"
      << "\n  Size: " << AMsg.Size
      << "\n  Pos 0 is 'AtomValue': " << AMsg.isTypeAt<AtomValue>(0)
      << "\n  Pos 0 is 'AtomConstant<atom(\"noatom\")>': "
      << AMsg.isTypeAt<MyNAtom>(0) << "\n\n";

  using MyAtomMatcher = MsgMatcher<MyAtom>;
  Log << "Matching against 'TypeList<AtomConstant<atom(\"atom\")>>':"
      << "\n  matching: " << MyAtomMatcher::doesStronglyMatch(AMsg)
      << "\n  value: '("
      << to_string(std::get<0>(MyAtomMatcher::extractedValues(AMsg))) << ")'"
      << "\n\n";

  using MyWrongAtomMatcher = MsgMatcher<MyNAtom>;
  Log << "Matching against 'TypeList<AtomConstant<atom(\"noatom\")>>':"
      << "\n  matching: " << MyWrongAtomMatcher::doesStronglyMatch(AMsg)
      << "\n\n";

  // Invoker.
  auto IP = Invoker::wrap(Invoker::F<MyAtom>([&Log](MyAtom) noexcept->void {
    Log << "** Handling 'Message with "
           "TypeList<AtomConstant<atom(\"atom\")>>'.\n";
  }));
  auto &I = *IP; // Get a reference from the pointer.
  Log << "Invoking a function of signature 'void(AtomConstant<atom(\"atom\")>) "
         "noexcept':"
      << "\n  with 'Message with TypeList<uint8_t, uint16_t>'"
      << "\n    does Message match Invoker: " << I.match(Msg)
      << "\n    invoking...";
  I(Msg);
  Log << "\n  with 'Message with TypeList<AtomConstant<atom(\"atom\")>>'..."
      << "\n    does Message match Invoker: " << I.match(AMsg)
      << "\n    invoking...";
  I(AMsg);
  Log << "\n\n";

  // MessageHandler.
  MessageHandler Handler{
      Invoker::F<uint8_t, uint16_t>([&Log](uint8_t, uint16_t) {
        Log << "** Handling 'Message with TypeList<uint8_t, uint16_t>'\n";
      }),
      Invoker::F<MyAtom>([&Log](MyAtom) {
        Log << "** Handling 'Message with "
               "TypeList<AtomConstant<atom(\"atom\")>>'\n";
      })};
  auto PANMsg = Message::create(MyNAtom::Value);
  auto &ANMsg = *PANMsg;
  Log << "Handling Messages with 'MessageHandler "
         "{ Invoker::F<uint8_t, uint16_t>, "
         "Invoker::F<AtomConstant<atom(\"atom\")>> }':"
      << "\n  'Message with TypeList<uint8_t, uint16_t>'"
      << "\n    can handle: " << Handler.canHandle(Msg) << "\n    handling...";
  Handler(Msg);
  Log << "\n  'Message with TypeList<AtomConstant<atom(\"atom\")>>'"
      << "\n    can handle: " << Handler.canHandle(AMsg) << "\n    handling...";
  Handler(AMsg);
  Log << "\n  'Message with TypeList<AtomConstant<atom(\"noatom\")>>'"
      << "\n    can handle: " << Handler.canHandle(ANMsg)
      << "\n    handling...";
  Handler(ANMsg);
  Log << "\n\n";

  Log << "Terminating, destroying automatic variables.\n";

  return 0;
}
