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

#if defined(__ANDROID__)
#undef __ANDROID__
#endif

#include "catch.hpp"

#include "rotor-light/queue.hpp"

using namespace rotor_light;

struct StringMessage : Message {
  static constexpr MessageTypeId type_id = __LINE__;
  StringMessage(ActorId to, const char *msg_) : Message(to), string{msg_} {}
  const char *string;
};

using MessageStorage = traits::MessageStorage<StringMessage>;

void reset(ItemGuard &guard) { guard.item_queue->release(); }

TEST_CASE("1 priority, 1 message queue", "[queue]") {
  using AppQueue = Queue<MessageStorage, 1>;
  AppQueue queue;
  printf("queue sz = %lu, message sz = %lu\n", sizeof(queue),
         MessageStorage::item_size);

  CHECK(queue.put<StringMessage>(0, 1, "hello"));
  CHECK(!queue.put<StringMessage>(0, 1, "world"));

  auto msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("hello"));
  reset(msg);

  CHECK(queue.put<StringMessage>(0, 1, "world"));
  CHECK(!queue.put<StringMessage>(0, 1, "hello"));
  msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("world"));

  reset(msg);
  CHECK(!queue.next());
}

TEST_CASE("1 priority, 2 messages queue", "[queue]") {
  using AppQueue = Queue<MessageStorage, 2>;
  AppQueue queue;
  printf("queue sz = %lu, message sz = %lu\n", sizeof(queue),
         MessageStorage::item_size);

  CHECK(queue.put<StringMessage>(0, 1, "hello"));
  CHECK(queue.put<StringMessage>(0, 1, "world"));
  CHECK(!queue.put<StringMessage>(0, 1, "!"));

  auto msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("hello"));
  reset(msg);

  msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("world"));
  reset(msg);

  CHECK(queue.put<StringMessage>(0, 1, "world"));
  CHECK(queue.put<StringMessage>(0, 1, "hello"));
  CHECK(!queue.put<StringMessage>(0, 1, "!"));

  msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("world"));
  reset(msg);

  msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("hello"));
  reset(msg);

  CHECK(!queue.next());
}

TEST_CASE("1 priority, 3 messages queue", "[queue]") {
  using AppQueue = Queue<MessageStorage, 3>;
  AppQueue queue;

  SECTION("bugfix") {
    CHECK(queue.put<StringMessage>(0, 1, "hello"));
    auto msg = queue.next();
    CHECK(static_cast<StringMessage &>(*msg).string == std::string("hello"));

    CHECK(queue.put<StringMessage>(0, 1, "world"));
    reset(msg);
    msg = queue.next();
    CHECK(static_cast<StringMessage &>(*msg).string == std::string("world"));
  }
}

TEST_CASE("2 priorities, 2/1 messages", "[queue]") {
  using AppQueue = Queue<MessageStorage, 2, 1>;
  AppQueue queue;
  printf("queue sz = %lu, message sz = %lu\n", sizeof(queue),
         MessageStorage::item_size);

  SECTION("order-1") {
    CHECK(queue.put<StringMessage>(0, 1, "lorem"));
    CHECK(queue.put<StringMessage>(0, 1, "ipsum"));
    CHECK(queue.put<StringMessage>(1, 1, "dolor"));
  }

  SECTION("order-2") {
    CHECK(queue.put<StringMessage>(1, 1, "dolor"));
    CHECK(queue.put<StringMessage>(0, 1, "lorem"));
    CHECK(queue.put<StringMessage>(0, 1, "ipsum"));
  }

  SECTION("order-3") {
    CHECK(queue.put<StringMessage>(0, 1, "lorem"));
    CHECK(queue.put<StringMessage>(1, 1, "dolor"));
    CHECK(queue.put<StringMessage>(0, 1, "ipsum"));
  }

  auto msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("dolor"));

  reset(msg);
  msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("lorem"));

  reset(msg);
  msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("ipsum"));

  reset(msg);
  CHECK(!queue.next());
}
