#include "tile/base/dependency_registry.h"
#include "gtest/gtest.h"

namespace tile {
struct Destroyer {
    virtual ~Destroyer() = default;
};

struct FastDestroyer : Destroyer {
    FastDestroyer() { ++instances; }

    ~FastDestroyer() override { --instances; }

    static int instances;
};

int FastDestroyer::instances = 0;

struct GentleDestroyer : Destroyer {
    GentleDestroyer() { ++instances; }

    ~GentleDestroyer() override { --instances; }

    static int instances;
};

int GentleDestroyer::instances = 0;

struct SpeedDestroyer : Destroyer {
    explicit SpeedDestroyer(int) {}
};

struct SpeedDestroyer2 : Destroyer {};

TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(world_destroyer, Destroyer);
TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(world_destroyer, Destroyer);
TILE_REGISTER_CLASS_DEPENDENCY(world_destroyer, "fast-destroyer", FastDestroyer);
TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(world_destroyer, "gentle-destroyer", [] {
    return make_unique<GentleDestroyer>();
});
GentleDestroyer global_gentle_destroyer;

TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(with_arg_destroyer, Destroyer, int);
TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(with_arg_destroyer, Destroyer, int);
TILE_REGISTER_CLASS_DEPENDENCY(with_arg_destroyer, "speed-destroyer", SpeedDestroyer, int);
TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(with_arg_destroyer, "speed-destroyer-2", [](int speed) {
    return make_unique<SpeedDestroyer2>();
});

TEST(DependencyRegistry, Class)
{
    EXPECT_TRUE(world_destroyer.TryGetFactory("gentle-destroyer"));
    EXPECT_TRUE(world_destroyer.TryGetFactory("fast-destroyer"));
    EXPECT_FALSE(world_destroyer.TryGetFactory("404-destroyer"));
    EXPECT_TRUE(world_destroyer.TryNew("gentle-destroyer"));
    EXPECT_TRUE(world_destroyer.TryNew("fast-destroyer"));
    EXPECT_FALSE(world_destroyer.TryGetFactory("404-destroyer"));

    EXPECT_EQ(1, GentleDestroyer::instances);// The global one.
    EXPECT_EQ(0, FastDestroyer::instances);

    {
        auto gentle = world_destroyer.TryNew("gentle-destroyer");
        EXPECT_EQ(2, GentleDestroyer::instances);
        EXPECT_EQ(0, FastDestroyer::instances);
        auto fast = world_destroyer.TryNew("fast-destroyer");
        EXPECT_EQ(2, GentleDestroyer::instances);
        EXPECT_EQ(1, FastDestroyer::instances);
    }
    EXPECT_EQ(1, GentleDestroyer::instances);
    EXPECT_EQ(0, FastDestroyer::instances);
}

TEST(DependencyRegistry, ClassWithArgs)
{
    EXPECT_TRUE(with_arg_destroyer.TryGetFactory("speed-destroyer"));
    EXPECT_TRUE(with_arg_destroyer.TryGetFactory("speed-destroyer-2"));
    EXPECT_FALSE(with_arg_destroyer.TryGetFactory("speed-destroyer-3"));
    EXPECT_TRUE(with_arg_destroyer.TryNew("speed-destroyer", 456));
    EXPECT_TRUE(with_arg_destroyer.TryNew("speed-destroyer-2", 456));
    EXPECT_FALSE(with_arg_destroyer.TryNew("speed-destroyer-3", 456));
}

}// namespace tile
