/***************************************************************************//**
 *
 * \file examples/basic-system/basic-system.cpp
 *
 * \author David Juhasz (david.juhasz@tuwien.ac.at)
 *
 * \date 2017
 *
 * \brief A simple example on the basic `rosa::System` and `rosa::Unit` classes.
 *
 ******************************************************************************/

#include "rosa/config/version.h"

#include "rosa/core/Unit.h"
#include "rosa/core/System.hpp"

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

#include <iostream>

using namespace rosa;
using namespace rosa::terminal;

/// A dummy wrapper for testing `rosa::MessagingSystem`.
///
/// \note Since we test `rosa::MessagingSystem` directly here, we need to get
/// access to its protected members. That we do by imitating to be a decent
/// subclass of `rosa::MessagingSystem`, while calling protected member
/// functions on an object of a type from which we actually don't inherit.
struct SystemTester : protected System {
  static constexpr AtomValue UnitKind = atom("unit");

  static Unit &createMyUnit(System *S,
                            const std::string &Name = std::string()) {
    return ((SystemTester *)S)
        ->createUnit<Unit, System>([&Name](const rosa::id_t Id,
                                           System &S) noexcept {
          // \note If `Name` is empty, construct a name with the number of the
          // `rosa::Unit` instance being created right now.
          const std::string N(
              Name.empty()
                  ? "Unit_" + std::to_string(S.numberOfConstructedUnits())
                  : Name);
          return new Unit(UnitKind, Id, N, S);
        });
  }

  static void destroyMyUnit(System *S, Unit &U) {
    ((SystemTester *)S)->destroyUnit(U);
  }
};

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

  std::unique_ptr<System> S = System::createSystem("Sys");
  System *SP = S.get();
  Unit &Unit1 = SystemTester::createMyUnit(SP),
       &Unit2 = SystemTester::createMyUnit(SP, "Second"),
       &Unit3 = SystemTester::createMyUnit(SP);
  SystemTester::destroyMyUnit(SP, Unit1);
  SystemTester::destroyMyUnit(SP, Unit3);
  LOG_INFO_STREAM << "Dumping Unit2" << std::endl << Unit2 << std::endl;
  SystemTester::destroyMyUnit(SP, Unit2);
  return 0;
}

