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

TimePoint now = 1;
TimePoint get_current_time() { return ++now; }

TEST_CASE("trigger some timers, no refresher", "[timers]") {
  static int triggered{0};

  struct A : Actor<1> {
    using Parent = Actor<1>;
    using Parent::Parent;
    virtual void advance_init() {
      add_event<ctx::thread>(
          now, [](void *data) { reinterpret_cast<A *>(data)->on_timer(); },
          this);
      add_event<ctx::thread>(
          50, [](void *) { ++triggered; }, this);
    }
    void on_timer() { Parent::advance_init(); }
  };

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

  AppQueue queue;
  AppPlanner planner;
  Context context{&queue, &planner, &get_current_time};
  S sup;
  sup.bind(context);
  auto act = sup.get_child<0>();

  sup.start();
  sup.process();
  CHECK(sup.get_state() == State::operational);
  CHECK(act->get_state() == State::operational);
  CHECK(triggered == 0);
  CHECK(planner.next_event());

  sup.stop();
  sup.process();
}

TEST_CASE("trigger some timers, with time refresher", "[timers]") {
  struct A : Actor<1> {
    using Parent = Actor<1>;
    using Parent::Parent;
    virtual void advance_init() {
      add_event<ctx::thread>(
          now + 100,
          [](void *data) { reinterpret_cast<A *>(data)->on_timer(); }, this);
    }
    void on_timer() { Parent::advance_init(); }
  };

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

    void on_refhesh_timer(message::RefreshTime &message) override {
      if (state < State::operational) {
        Parent::on_refhesh_timer(message);
      }
    }
  };

  AppQueue queue;
  AppPlanner planner;
  Context context{&queue, &planner, &get_current_time};
  S sup;
  sup.bind(context);
  auto act = sup.get_child<0>();

  sup.start(true);
  sup.process();
  CHECK(sup.get_state() == State::operational);
  CHECK(act->get_state() == State::operational);
  CHECK(!planner.next_event());

  sup.stop();
  sup.process();
}
