#include "tile/base/internal/logging.h"

#include "tile/base/deferred.h"

// #include "glog/logging.h"
#include "gmock/gmock-matchers.h"
#include "gtest/gtest.h"

#include <thread>

struct AwesomeLogSink : public tile::LogSink {
    static std::shared_ptr<AwesomeLogSink> Create() { return std::make_shared<AwesomeLogSink>(); }

    const char *name() const override { return "AwesomeLogSink"; }

    void Send(tile::LogSeverity severity,
              const char *full_filename,
              const char *base_filename,
              int line,
              const tile::LogMessageTime &logmsgtime,
              const char *message,
              size_t message_len) override
    {
        msgs.emplace_back(std::string(message, message_len));
    }

    std::vector<std::string> msgs;
};

std::string my_prefix, my_prefix2;

void
ResetLogPrefix()
{
    my_prefix.clear();
    my_prefix2.clear();
}

void
WriteLoggingPrefix(std::string *s)
{
    *s += my_prefix;
}

void
WriteLoggingPrefix2(std::string *s)
{
    *s += my_prefix2;
}

TEST(Logging, Prefix)
{
    auto sink = std::make_shared<AwesomeLogSink>();
    tile::AddLogSink(sink);
    tile::ScopedDeferred defered([&] { tile::RemoveLogSink(sink); });

    TILE_LOG_INFO("something");

    my_prefix = "[prefix]";
    TILE_LOG_INFO("something");

    my_prefix = "[prefix1]";
    TILE_LOG_INFO("something");

    my_prefix2 = "[prefix2]";
    TILE_LOG_INFO("something");

    ASSERT_THAT(sink->msgs,
                ::testing::ElementsAre(
                    "something", "[prefix] something", "[prefix1] something", "[prefix1] [prefix2] something"));
}

TEST(Logging, CHECK)
{
    ASSERT_DEATH(([] { TILE_CHECK(false, "CHECK"); }()), "CHECK");
    ASSERT_DEATH(([] { TILE_CHECK_LE(2, 1, "CHECK_LE"); }()), "CHECK_LE");
    ASSERT_DEATH(([] { TILE_CHECK_LT(2, 1, "CHECK_LT"); }()), "CHECK_LT");
    ASSERT_DEATH(([] { TILE_CHECK_GE(1, 2, "CHECK_GE"); }()), "CHECK_GE");
    ASSERT_DEATH(([] { TILE_CHECK_GT(1, 2, "CHECK_GT"); }()), "CHECK_GT");
    ASSERT_DEATH(([] { TILE_CHECK_EQ(1, 2, "CHECK_EQ"); }()), "CHECK_EQ");
    ASSERT_DEATH(([] { TILE_CHECK_NE(1, 1, "CHECK_NE"); }()), "CHECK_NE");
}

TEST(Logging, DontPanicOnFormatFailure) { TILE_LOG_INFO("Don't panic!{}{}", 1); }

TEST(Logging, EverySecond)
{
    ResetLogPrefix();

    auto sink = AwesomeLogSink::Create();
    tile::AddLogSink(sink);
    tile::ScopedDeferred defered([&] { tile::RemoveLogSink(sink); });
    for (int i = 0; i < 30; i++) {
        TILE_LOG_INFO_EVERY_SECOND("something");
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    ASSERT_THAT(sink->msgs, ::testing::ElementsAre("something", "something", "something"));
}

TEST(Logging, Enum)
{
    enum MyEnum { A, B, C };

    MyEnum e = MyEnum::A;
    TILE_LOG_INFO("{}", e);
}

TEST(Logging, EnumClass)
{
    enum MyEnumClass { A, B, C };

    MyEnumClass e = MyEnumClass::A;
    TILE_LOG_INFO("{}", e);
}

TILE_INTERNAL_LOGGING_REGISTER_PREFIX_PROVIDER(0, WriteLoggingPrefix)
TILE_INTERNAL_LOGGING_REGISTER_PREFIX_PROVIDER(1, WriteLoggingPrefix2)
