#include <gtest/gtest.h>
#include <gmock/gmock.h>

#include "core/object/ObjectListIterator.h"
#include "core/object/Listable.h"
#include "core/CoreIncludes.h"
#include "core/module/ModuleInstance.h"

namespace orxonox
{
    namespace
    {
        class ListableTest : public Listable
        {
            public:
                ListableTest() { RegisterObject(ListableTest); }
                ListableTest(Context* context) : Listable(context) { RegisterObject(ListableTest); }
                MOCK_METHOD0(test, void());
        };

        RegisterClassNoArgs(ListableTest);

        // Fixture
        class ObjectListTest : public ::testing::Test
        {
            public:
                virtual void SetUp() override
                {
                    new IdentifierManager();
                    ModuleInstance::getCurrentModuleInstance()->loadAllStaticallyInitializedInstances(StaticInitialization::IDENTIFIER);
                    Context::setRootContext(new Context(nullptr));
                }

                virtual void TearDown() override
                {
                    Context::destroyRootContext();
                    ModuleInstance::getCurrentModuleInstance()->unloadAllStaticallyInitializedInstances(StaticInitialization::IDENTIFIER);
                    delete &IdentifierManager::getInstance();
                }
        };
    }
    
    TEST_F(ObjectListTest, CanIterateOverEmptyList)
    {
        size_t i = 0;
        for (ListableTest* object : ObjectList<ListableTest>())
        {
            ++i;
            ((void*)object); // avoid warning about unused variable
        }
        EXPECT_EQ(0u, i);
    }

    TEST_F(ObjectListTest, CanIterateOverFullList)
    {
        ListableTest test1;
        ListableTest test2;
        ListableTest test3;

        size_t i = 0;
        for (ListableTest* object : ObjectList<ListableTest>())
        {
            ++i;
            if (i == 1u) EXPECT_EQ(&test1, object);
            if (i == 2u) EXPECT_EQ(&test2, object);
            if (i == 3u) EXPECT_EQ(&test3, object);
        }
        EXPECT_EQ(3u, i);
    }
    
    TEST_F(ObjectListTest, CanIterateOverFullList_Expanded)
    {
        ListableTest test1;
        ListableTest test2;
        ListableTest test3;

        size_t i = 0;
        ObjectList<ListableTest>&& __range = ObjectList<ListableTest>();
        ObjectListIterator<ListableTest> __begin = __range.begin();
        ObjectListIterator<ListableTest>__end = __range.end();
        for (; __begin != __end; ++__begin)
        {
            ListableTest* object = *__begin;

            ++i;
            if (i == 1u) EXPECT_EQ(&test1, object);
            if (i == 2u) EXPECT_EQ(&test2, object);
            if (i == 3u) EXPECT_EQ(&test3, object);
        }
        EXPECT_EQ(3u, i);
    }
    
    TEST_F(ObjectListTest, CanCallObjects)
    {
        ListableTest test1;
        ListableTest test2;
        ListableTest test3;

        EXPECT_CALL(test1, test());
        EXPECT_CALL(test2, test());
        EXPECT_CALL(test3, test());

        for (ListableTest* object : ObjectList<ListableTest>())
            object->test();
    }

    TEST_F(ObjectListTest, UsesCorrectContext)
    {
        Context context1(Context::getRootContext());
        Context context2(Context::getRootContext());

        ListableTest test1(&context1);
        ListableTest test2(&context1);
        ListableTest test3(&context2);

        EXPECT_EQ(3u, ObjectList<ListableTest>().size());
        EXPECT_EQ(2u, ObjectList<ListableTest>(&context1).size());
        EXPECT_EQ(1u, ObjectList<ListableTest>(&context2).size());
    }

    TEST_F(ObjectListTest, CanIterateOverCorrectContext)
    {
        Context context1(Context::getRootContext());
        Context context2(Context::getRootContext());

        ListableTest test1(&context1);
        ListableTest test2(&context1);
        ListableTest test3(&context2);

        {
            size_t i = 0;
            for (ListableTest* object : ObjectList<ListableTest>(&context1))
            {
                ++i;
                if (i == 1u) EXPECT_EQ(&test1, object);
                if (i == 2u) EXPECT_EQ(&test2, object);
            }
            EXPECT_EQ(2u, i);
        }
        {
            size_t i = 0;
            for (ListableTest* object : ObjectList<ListableTest>(&context2))
            {
                ++i;
                if (i == 1u) EXPECT_EQ(&test3, object);
            }
            EXPECT_EQ(1u, i);
        }
    }
}
