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

#include "core/object/ObjectListIterator.h"
#include "core/class/OrxonoxClass.h"
#include "core/class/OrxonoxInterface.h"
#include "core/CoreIncludes.h"
#include "core/module/ModuleInstance.h"

namespace orxonox
{
    namespace
    {
        class TestInterface : virtual public OrxonoxInterface
        {
            public:
                TestInterface() { RegisterObject(TestInterface); }
        };

        class TestClass : public OrxonoxClass, public TestInterface
        {
            public:
                TestClass() { RegisterObject(TestClass); }
                MOCK_METHOD0(test, void());
        };

        RegisterClassNoArgs(TestInterface);
        RegisterClassNoArgs(TestClass);

        // Fixture
        class ObjectListIteratorTest : 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(ObjectListIteratorTest, CanCreateIterator)
    {
        ObjectListIterator<TestClass> it;
    }

    TEST_F(ObjectListIteratorTest, CanAssignIterator)
    {
        ObjectList<TestClass> list;
        ObjectListIterator<TestClass> it = list.begin();
    }

    TEST_F(ObjectListIteratorTest, CanIterateOverEmptyList)
    {
        size_t i = 0;
        ObjectList<TestClass> list;
        for (ObjectListIterator<TestClass> it = list.begin(); it != list.end(); ++it)
            ++i;
        EXPECT_EQ(0u, i);
    }

    TEST_F(ObjectListIteratorTest, CanIterateOverFullList)
    {
        TestClass test1;
        TestClass test2;
        TestClass test3;
        TestInterface interface;

        size_t i = 0;
        ObjectList<TestClass> list;
        for (ObjectListIterator<TestClass> it = list.begin(); it != list.end(); ++it)
        {
            ++i;
            if (i == 1u) EXPECT_EQ(&test1, *it);
            if (i == 2u) EXPECT_EQ(&test2, *it);
            if (i == 3u) EXPECT_EQ(&test3, *it);
        }
        EXPECT_EQ(3u, i);
    }

    TEST_F(ObjectListIteratorTest, CanIterateReverseOverFullList)
    {
        TestClass test1;
        TestClass test2;
        TestClass test3;
        TestInterface interface;

        size_t i = 0;
        ObjectList<TestClass> list;
        for (ObjectListIterator<TestClass> it = list.rbegin(); it != list.rend(); --it)
        {
            ++i;
            if (i == 1u) EXPECT_EQ(&test3, *it);
            if (i == 2u) EXPECT_EQ(&test2, *it);
            if (i == 3u) EXPECT_EQ(&test1, *it);
        }
        EXPECT_EQ(3u, i);
    }

    TEST_F(ObjectListIteratorTest, CanCallObjects)
    {
        TestClass test1;
        TestClass test2;
        TestClass test3;
        TestInterface interface;

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

        ObjectList<TestClass> list;
        for (ObjectListIterator<TestClass> it = list.begin(); it != list.end(); ++it)
            it->test();
    }
}
