// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2022 Ivan Baidakou

#if defined(__ANDROID__)
#undef __ANDROID__
#endif

#include "catch.hpp"

#include "rotor-light/planner.hpp"
#include "rotor-light/queue.hpp"
#include "rotor-light/supervisor.hpp"
#include <string>

using namespace rotor_light;

using MessageStorage =
    traits::MessageStorage<message::ChangeState, message::ChangeStateAck>;
using AppQueue = Queue<MessageStorage, 5>;
using AppPlanner = Planner<1>;

static std::string order;

void append(std::string_view marker, State state) {
  std::string_view s = "/unknown/";
  switch (state) {
  case State::initialized:
    s = "/initialized/";
    break;
  case State::initializing:
    s = "/initializing/";
    break;
  case State::operational:
    s = "/operational/";
    break;
  case State::shutting_down:
    s = "/shutting_down/";
    break;
  case State::off:
    s = "/off/";
    break;
  }
  order += marker;
  order += s;
  order += ", ";
}

struct A : Actor<1> {
  using Parent = Actor<1>;
  using Parent::Parent;
  void advance_init() override {
    append("a:init", state);
    Parent::advance_init();
    append("a:init", state);
  };

  void advance_start() override {
    append("a:start", state);
    Parent::advance_start();
    append("a:start", state);
  }

  void advance_stop() override {
    append("a:stop", state);
    Parent::advance_stop();
    append("a:stop", state);
  }
};

struct S : Supervisor<3, A> {
  using Parent = Supervisor<3, A>;
  using Parent::Parent;

  void advance_init() override {
    append("s:init", state);
    Parent::advance_init();
    append("s:init", state);
  };

  void advance_start() override {
    append("s:start", state);
    Parent::advance_start();
    append("s:start", state);
  }

  void advance_stop() override {
    append("s:stop", state);
    Parent::advance_stop();
    append("s:stop", state);
  }
};

TEST_CASE("actor & supervisor events order", "[sup]") {
  AppQueue queue;
  AppPlanner planner;
  Context context{&queue, &planner, nullptr};
  S sup;
  sup.bind(context);
  sup.start();
  sup.process();
  // clang-format off
  CHECK(order == "s:init/initializing/, "
                 "s:init/initializing/, "
                 "a:init/initializing/, "
                 "a:init/initialized/, "
                 "s:start/operational/, "
                 "s:start/operational/, "
                 "a:start/operational/, "
                 "a:start/operational/, ");
  // clang-format on

  order = "";
  sup.stop();
  sup.process();
  // clang-format off
    CHECK(order == "s:stop/shutting_down/, "
                   "s:stop/shutting_down/, "
                   "a:stop/shutting_down/, "
                   "a:stop/off/, ");
  // clang-format on
}
