// 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;

struct Signal : Message {
  static constexpr auto type_id = __LINE__;
  using Message::Message;
};

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

struct A : Actor<2> {
  using Parent = Actor<2>;
  using Parent::Parent;

  void initialize() override {
    Parent::initialize();
    subscribe(&A::on_signal);
  }

  void on_signal(Signal &) { ++count; }

  int count = 0;
};

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

  void initialize() override {
    Parent::initialize();
    subscribe(&S::on_signal);
  }

  void on_signal(Signal &) { ++count; }

  void advance_start() override {
    Parent::advance_start();
    send_signal();
  }

  void send_signal() { send<ctx::thread, Signal>(0, broadcast); }

  int count = 0;
};

TEST_CASE("broadcasting example", "[sup]") {
  AppQueue queue;
  AppPlanner planner;
  Context context{&queue, &planner, nullptr};
  S sup;
  sup.bind(context);
  auto act1 = sup.get_child<0>();
  auto act2 = sup.get_child<1>();
  sup.start();
  sup.process();

  REQUIRE(sup.get_state() == State::operational);
  REQUIRE(act1->get_state() == State::operational);
  REQUIRE(act2->get_state() == State::operational);

  CHECK(sup.count == 1);
  CHECK(act1->count == 1);
  CHECK(act2->count == 1);

  act2->stop();
  sup.process();
  REQUIRE(act2->get_state() == State::off);

  sup.send_signal();
  sup.process();

  CHECK(sup.count == 2);
  CHECK(act1->count == 2);
  CHECK(act2->count == 1);

  sup.stop();
  sup.process();
  CHECK(sup.get_state() == State::off);
  CHECK(act1->get_state() == State::off);
  CHECK(act2->get_state() == State::off);
}
