/*******************************************************************************
 *
 * File:     messaging.cpp
 *
 * Contents: An example showcasing features related to Messages.
 *
 * Copyright 2017
 *
 * Author: David Juhasz (david.juhasz@tuwien.ac.at)
 *
 ******************************************************************************/

#include "rosa/config/version.h"

#include "rosa/core/MessageHandler.hpp"

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

#include <iostream>

using namespace rosa;
using namespace rosa::terminal;

int main(void) {
  LOG_INFO_STREAM << library_string() << " -- " << Color::Red
                  << "messaging" << Color::Default << std::endl;

  auto &Log = LOG_INFO_STREAM << std::endl;

  // 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>>':"
      << std::endl
      << "  Size: " << Msg.Size << std::endl
      << "  Pos 0 is uint8_t: " << Msg.isTypeAt<uint8_t>(0) << std::endl
      << "  Pos 1 is uint16_t: " << Msg.isTypeAt<uint16_t>(1) << std::endl
      << "  Pos 2 is uint32_t: " << Msg.isTypeAt<uint32_t>(1) << std::endl
      << "  Value at pos 0: " << PRINTABLE(Msg.valueAt<uint8_t>(0)) << std::endl
      << "  Value at pos 1: " << Msg.valueAt<uint16_t>(1) << std::endl
      << std::endl;

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

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

  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\")>>':"
      << std::endl
      << "  Size: " << AMsg.Size << std::endl
      << "  Pos 0 is 'AtomValue': " << AMsg.isTypeAt<AtomValue>(0) << std::endl
      << "  Pos 0 is 'AtomConstant<atom(\"noatom\")>': "
      << AMsg.isTypeAt<MyNAtom>(0) << std::endl
      << std::endl;

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

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

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

  // MessageHandler.
  MessageHandler Handler{
      Invoker::F<uint8_t, uint16_t>([&Log](uint8_t, uint16_t) {
        Log << "** Handling 'Message with TypeList<uint8_t, uint16_t>'"
            << std::endl;
      }),
      Invoker::F<MyAtom>([&Log](MyAtom) {
        Log << "** Handling 'Message with "
               "TypeList<AtomConstant<atom(\"atom\")>>'" << std::endl;
      })};
  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\")>> }':"
      << std::endl
      << "  'Message with TypeList<uint8_t, uint16_t>'" << std::endl
      << "    can handle: " << Handler.canHandle(Msg) << std::endl
      << "    handling..." << std::endl;
  Handler(Msg);
  Log << "  'Message with TypeList<AtomConstant<atom(\"atom\")>>'" << std::endl
      << "    can handle: " << Handler.canHandle(AMsg) << std::endl
      << "    handling..." << std::endl;
  Handler(AMsg);
  Log << "  'Message with TypeList<AtomConstant<atom(\"noatom\")>>'"
      << std::endl
      << "    can handle: " << Handler.canHandle(ANMsg) << std::endl
      << "    handling..." << std::endl;
  Handler(ANMsg);
  Log << std::endl;

  Log << "Terminating, destroying automatic variables." << std::endl;

  return 0;
}

