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

TEST_CASE("normal hierarchy", "[hierarchy]") {
  struct A1 : Actor<1> {
    using Parent = Actor<1>;
    using Parent::Parent;
  };

  struct S1_1 : Supervisor<3, A1> {
    using Parent = Supervisor<3, A1>;
    using Parent::Parent;
  };

  struct S1_2 : Supervisor<3, S1_1> {
    using Parent = Supervisor<3, S1_1>;
    using Parent::Parent;
  };

  AppQueue queue;
  AppPlanner planner;
  Context context{&queue, &planner, nullptr};
  S1_2 sup;
  sup.bind(context);
  auto sup2 = sup.get_child<0>();
  auto act = sup2->get_child<0>();

  sup.start();
  sup.process();

  CHECK(sup.get_state() == State::operational);
  CHECK(sup2->get_state() == State::operational);
  CHECK(act->get_state() == State::operational);

  SECTION("stop root supervisor") {
    sup.stop();
    sup.process();

    CHECK(sup.get_state() == State::off);
    CHECK(sup2->get_state() == State::off);
    CHECK(act->get_state() == State::off);
  }

  SECTION("manual stop") {
    sup2->stop();
    sup.process();

    CHECK(sup.get_state() == State::operational);
    CHECK(sup2->get_state() == State::off);
    CHECK(act->get_state() == State::off);

    sup.stop();
    sup.process();
    CHECK(sup.get_state() == State::off);
  }
}

TEST_CASE("auto collapse hierarchy", "[hierarchy]") {
  struct A1 : Actor<1> {
    using Parent = Actor<1>;
    using Parent::Parent;
  };

  struct A2 : Actor<1> {
    using Parent = Actor<1>;
    using Parent::Parent;
    void advance_init() override {
      send<ctx::thread, message::ChangeStateAck>(0, supervisor->get_id(), id,
                                                 State::initialized, false);
      init_attempted = true;
    }
    bool init_attempted = false;
  };

  struct S1_1 : Supervisor<3, A1, A2> {
    using Parent = Supervisor<3, A1, A2>;
    using Parent::Parent;
  };

  struct S1_2 : Supervisor<3, S1_1> {
    using Parent = Supervisor<3, S1_1>;
    using Parent::Parent;
  };

  AppQueue queue;
  AppPlanner planner;
  Context context{&queue, &planner, nullptr};
  S1_2 sup;
  sup.bind(context);
  auto sup2 = sup.get_child<0>();
  auto act1 = sup2->get_child<0>();
  auto act2 = sup2->get_child<1>();

  sup.start();
  sup.process();

  CHECK(sup.get_state() == State::off);
  CHECK(sup2->get_state() == State::off);
  CHECK(act1->get_state() == State::off);
  CHECK(act2->get_state() == State::off);
  REQUIRE(act2->init_attempted);
}
