#include "pch.h"

#define NO_MAIN
#include "Array.h"
#include "CppUnitTest.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace FrameworkTests
{
    TEST_CLASS (ArrayTests)
    {
    public:
        TEST_METHOD (EmtptyTest)
        {
            Framework::Array<int> array;
            Assert::IsTrue(array.getEintragAnzahl() == 0,
                L"getEintragAnzahl() on empty array should be 0");
            int count = 0;
            for (auto i : array)
                count++;
            Assert::IsTrue(count == 0,
                L"Iteration with : over an empty array should not do anything");
            auto end = array.end();
            Assert::IsFalse(array.begin() != end,
                L"Iterator of an empty array should not have an element");
            Assert::IsFalse(
                array.hat(0), L"Empty Array should not have an element");
        }

        TEST_METHOD (AddTest)
        {
            Framework::Array<int> array;
            array.add(100);
            array.add(20, 1);
            array.add(10, 0);
            array.add(0);
            Assert::IsTrue(array.getEintragAnzahl() == 4,
                L"after adding 4 elements getEintragAnzahl() should be 4");
            Assert::IsTrue(array.get(0) == 10,
                L"invalid value at index 0 of array after adding elements");
            Assert::IsTrue(array.get(1) == 100,
                L"invalid value at index 1 of array after adding elements");
            Assert::IsTrue(array.get(2) == 20,
                L"invalid value at index 1 of array after adding elements");
            Assert::IsTrue(array.get(3) == 0,
                L"invalid value at index 1 of array after adding elements");
            auto i = array.begin();
            Assert::IsTrue(i.val() == 10,
                L"invalid value at index 0 of array after adding elements");
            i++;
            Assert::IsTrue(i.val() == 100,
                L"invalid value at index 1 of array after adding elements");
            ++i;
            Assert::IsTrue(i.val() == 20,
                L"invalid value at index 2 of array after adding elements");
            i = i.next();
            Assert::IsTrue(i.val() == 0,
                L"invalid value at index 3 of array after adding elements");
            Assert::IsFalse(i.hasNext(),
                L"Iterator has to much elements after adding elements to "
                L"array");
            auto end = array.end();
            Assert::IsFalse(++i != end,
                L"iterator should match end() after iterating throu the array");
        }

        TEST_METHOD (RemoveTest)
        {
            Framework::Array<int> array;
            array.add(100);
            array.add(20, 1);
            array.add(10, 0);
            array.add(0);
            array.remove(3);
            array.remove(1);
            array.remove(0);
            Assert::IsTrue(array.getEintragAnzahl() == 1,
                L"after adding 4 elements and removing 3 elements "
                L"getEintragAnzahl() should be 1");
            Assert::IsTrue(array.get(0) == 20,
                L"invalid value at index 0 of array after removing elements");
            Assert::IsTrue(array.begin().val() == 20,
                L"invalid value at index 0 of array after removing elements");
            Assert::IsFalse(array.begin().hasNext(),
                L"Iterator has to many elements after removing elements");
            auto end = array.end();
            Assert::IsFalse(array.begin().next() != end,
                L"Iterator has to many elements after removing elements");
        }

        TEST_METHOD (SwapTest)
        {
            Framework::Array<int> array;
            array.add(100);
            array.add(20, 1);
            array.add(10, 0);
            array.add(0);
            array.tausch(0, 3);
            array.tausch(1, 2);
            Assert::IsTrue(array.getEintragAnzahl() == 4,
                L"after adding 4 elements and swap elements getEintragAnzahl() "
                L"should be 4");
            Assert::IsTrue(array.get(0) == 0,
                L"invalid value at index 0 of array after swapping elements");
            Assert::IsTrue(array.get(1) == 20,
                L"invalid value at index 1 of array after swapping elements");
            Assert::IsTrue(array.get(2) == 100,
                L"invalid value at index 2 of array after swapping elements");
            Assert::IsTrue(array.get(3) == 10,
                L"invalid value at index 3 of array after swapping elements");
            array.tausch(2, 0);
            Assert::IsTrue(array.get(2) == 0,
                L"invalid value at index 2 of array after swapping elements");
            Assert::IsTrue(array.get(0) == 100,
                L"invalid value at index 0 of array after swapping elements");
        }

        TEST_METHOD (SetTest)
        {
            Framework::Array<int> array;
            array.add(100);
            array.add(20, 1);
            array.add(10, 0);
            array.add(0);
            array.set(30, 0);
            array.set(200, 3);
            array.set(500, 1);
            Assert::IsTrue(array.getEintragAnzahl() == 4,
                L"after adding 4 elements and changing elements "
                L"getEintragAnzahl() should be 4");
            Assert::IsTrue(array.get(0) == 30,
                L"invalid value at index 0 of array after swapping elements");
            Assert::IsTrue(array.get(1) == 500,
                L"invalid value at index 1 of array after swapping elements");
            Assert::IsTrue(array.get(2) == 20,
                L"invalid value at index 2 of array after swapping elements");
            Assert::IsTrue(array.get(3) == 200,
                L"invalid value at index 3 of array after swapping elements");
        }

        TEST_METHOD (ValueTest)
        {
            Framework::Array<int> array;
            array.add(100);
            array.add(20, 1);
            array.add(10, 0);
            array.add(0);
            array.removeValue(10);
            Assert::IsTrue(array.getEintragAnzahl() == 3,
                L"after adding 4 elements and removing elements by value "
                L"getEintragAnzahl() should be 3");
            Assert::IsTrue(array.getWertIndex(0) == 2,
                L"invalid value index of value after removing elements");
            Assert::IsTrue(array.getWertIndex(10) < 0,
                L"value is still in array after removeValue");
        }
    };

    template<typename T> class Test : public Framework::ReferenceCounter
    {
        int val = 0;
        T* tc;

    public:
        Test(const int v, T* tc)
            : ReferenceCounter(),
              val(v),
              tc(tc)
        {}

        ~Test()
        {
            tc->deleteCounter++;
        }

        operator int()
        {
            return val;
        }

        int getVal()
        {
            return val;
        }
    };

    TEST_CLASS (RCArrayTests)
    {
    public:
        int deleteCounter = 0;
        TEST_METHOD_INITIALIZE(InitTest)
        {
            deleteCounter = 0;
        }

        TEST_METHOD (EmtptyTest)
        {
            Framework::RCArray<Test<RCArrayTests>> array;
            Assert::IsTrue(array.getEintragAnzahl() == 0,
                L"getEintragAnzahl() on empty array should be 0");
            int count = 0;
            for (auto i : array)
                count++;
            Assert::IsTrue(count == 0,
                L"Iteration with : over an empty array should not do anything");
            auto end = array.end();
            Assert::IsFalse(array.begin() != end,
                L"Iterator of an empty array should not have an element");
            Assert::IsFalse(
                array.hat(0), L"Empty Array should not have an element");
        }

        TEST_METHOD (AddTest)
        {
            Framework::RCArray<Test<RCArrayTests>> array;
            array.add(new Test<RCArrayTests>(100, this));
            array.add(new Test<RCArrayTests>(20, this), 1);
            array.add(new Test<RCArrayTests>(10, this), 0);
            array.add(new Test<RCArrayTests>(0, this));
            Assert::IsTrue(array.getEintragAnzahl() == 4,
                L"after adding 4 elements getEintragAnzahl() should be 4");
            Assert::IsTrue((int)*array.z(0) == 10,
                L"invalid value at index 0 of array after adding elements");
            Assert::IsTrue((int)*array.z(1) == 100,
                L"invalid value at index 1 of array after adding elements");
            Assert::IsTrue((int)*array.z(2) == 20,
                L"invalid value at index 1 of array after adding elements");
            Assert::IsTrue((int)*array.z(3) == 0,
                L"invalid value at index 1 of array after adding elements");
            auto i = array.begin();
            Assert::IsTrue(i->getVal() == 10,
                L"invalid value at index 0 of array after adding elements");
            i++;
            Assert::IsTrue(i->getVal() == 100,
                L"invalid value at index 1 of array after adding elements");
            ++i;
            Assert::IsTrue(i->getVal() == 20,
                L"invalid value at index 2 of array after adding elements");
            i = i.next();
            Assert::IsTrue(i->getVal() == 0,
                L"invalid value at index 3 of array after adding elements");
            Assert::IsFalse(i.hasNext(),
                L"Iterator has to much elements after adding elements to "
                L"array");
            auto end = array.end();
            Assert::IsFalse(++i != end,
                L"iterator should match end() after iterating throu the array");
            array.leeren();
            Assert::IsTrue(deleteCounter == 4, L"Memory leaks detected");
        }

        TEST_METHOD (RemoveTest)
        {
            Framework::RCArray<Test<RCArrayTests>> array;
            array.add(new Test<RCArrayTests>(100, this));
            array.add(new Test<RCArrayTests>(20, this), 1);
            array.add(new Test<RCArrayTests>(10, this), 0);
            array.add(new Test<RCArrayTests>(0, this));
            array.remove(3);
            array.remove(1);
            array.remove(0);
            Assert::IsTrue(array.getEintragAnzahl() == 1,
                L"after adding 4 elements and removing 3 elements "
                L"getEintragAnzahl() should be 1");
            Assert::IsTrue((int)*array.z(0) == 20,
                L"invalid value at index 0 of array after removing elements");
            Assert::IsTrue(array.begin()->getVal() == 20,
                L"invalid value at index 0 of array after removing elements");
            Assert::IsFalse(array.begin().hasNext(),
                L"Iterator has to many elements after removing elements");
            auto end = array.end();
            Assert::IsFalse(array.begin().next() != end,
                L"Iterator has to many elements after removing elements");
            Assert::IsTrue(deleteCounter == 3, L"Memory leaks detected");
            array.leeren();
            Assert::IsTrue(deleteCounter == 4, L"Memory leaks detected");
        }

        TEST_METHOD (SwapTest)
        {
            Framework::RCArray<Test<RCArrayTests>> array;
            array.add(new Test<RCArrayTests>(100, this));
            array.add(new Test<RCArrayTests>(20, this), 1);
            array.add(new Test<RCArrayTests>(10, this), 0);
            array.add(new Test<RCArrayTests>(0, this));
            array.tausch(0, 3);
            array.tausch(1, 2);
            Assert::IsTrue(array.getEintragAnzahl() == 4,
                L"after adding 4 elements and swap elements getEintragAnzahl() "
                L"should be 4");
            Assert::IsTrue((int)*array.z(0) == 0,
                L"invalid value at index 0 of array after swapping elements");
            Assert::IsTrue((int)*array.z(1) == 20,
                L"invalid value at index 1 of array after swapping elements");
            Assert::IsTrue((int)*array.z(2) == 100,
                L"invalid value at index 2 of array after swapping elements");
            Assert::IsTrue((int)*array.z(3) == 10,
                L"invalid value at index 3 of array after swapping elements");
            array.tausch(2, 0);
            Assert::IsTrue((int)*array.z(2) == 0,
                L"invalid value at index 2 of array after swapping elements");
            Assert::IsTrue((int)*array.z(0) == 100,
                L"invalid value at index 0 of array after swapping elements");
            Assert::IsTrue(array.getEintragAnzahl() == 4,
                L"invalid value count in array after swapping elements");
            array.leeren();
            Assert::IsTrue(deleteCounter == 4, L"Memory leaks detected");
        }

        TEST_METHOD (SetTest)
        {
            Framework::RCArray<Test<RCArrayTests>> array;
            array.add(new Test<RCArrayTests>(100, this));
            array.add(new Test<RCArrayTests>(20, this), 1);
            array.add(new Test<RCArrayTests>(10, this), 0);
            array.add(new Test<RCArrayTests>(0, this));
            array.set(new Test<RCArrayTests>(30, this), 0);
            array.set(new Test<RCArrayTests>(200, this), 3);
            array.set(new Test<RCArrayTests>(500, this), 1);
            Assert::IsTrue(array.getEintragAnzahl() == 4,
                L"after adding 4 elements and changing elements "
                L"getEintragAnzahl() should be 4");
            Assert::IsTrue((int)*array.z(0) == 30,
                L"invalid value at index 0 of array after swapping elements");
            Assert::IsTrue((int)*array.z(1) == 500,
                L"invalid value at index 1 of array after swapping elements");
            Assert::IsTrue((int)*array.z(2) == 20,
                L"invalid value at index 2 of array after swapping elements");
            Assert::IsTrue((int)*array.z(3) == 200,
                L"invalid value at index 3 of array after swapping elements");
            Assert::IsTrue(deleteCounter == 3, L"Memory leaks detected");
            array.leeren();
            Assert::IsTrue(deleteCounter == 7, L"Memory leaks detected");
        }

        TEST_METHOD (IteratorTest)
        {
            Framework::RCArray<Test<RCArrayTests>> array;
            array.add(new Test<RCArrayTests>(100, this));
            array.begin().remove();
            Assert::IsTrue(array.getEintragAnzahl() == 0,
                L"after adding 1 element and removing it "
                L"getEintragAnzahl() should be 0");
            array.add(new Test<RCArrayTests>(100, this));
            array.add(new Test<RCArrayTests>(20, this), 1);
            array.add(new Test<RCArrayTests>(10, this), 0);
            array.add(new Test<RCArrayTests>(0, this));
            auto i = array.begin();
            Assert::IsTrue(i->getVal() == 10,
                L"invalid value at index 0 of array after adding elements");
            Assert::IsTrue(i.hasNext(), L"No next element after first element");
            Assert::IsTrue((++i)->getVal() == 100,
                L"invalid value at index 1 of array after adding elements");
            Assert::IsTrue(
                i.hasNext(), L"No next element after second element");
            Assert::IsTrue((++i)->getVal() == 20,
                L"invalid value at index 2 of array after adding elements");
            Assert::IsTrue(i.hasNext(), L"No next element after third element");
            Assert::IsTrue((++i)->getVal() == 0,
                L"invalid value at index 3 of array after adding elements");
            Assert::IsFalse(i.hasNext(), L"Next element after last element");
            i = array.begin();
            int count = 4;
            while (i)
            {
                i.remove();
                Assert::IsTrue(array.getEintragAnzahl() == --count,
                    L"after removing 1 element "
                    L"getEintragAnzahl() should be reduced");
            }
            Assert::IsTrue(deleteCounter == 5, L"Memory leaks detected");
        }

        TEST_METHOD (MoveTest)
        {
            Framework::RCArray<Test<RCArrayTests>> array;
            array.add(new Test<RCArrayTests>(100, this));
            array.add(new Test<RCArrayTests>(20, this));
            array.setPosition(0, 1);
            array.add(new Test<RCArrayTests>(10, this));
            Assert::IsTrue(array.getEintragAnzahl() == 3,
                L"after adding 3 elements "
                L"getEintragAnzahl() should be 0");
            Assert::IsTrue(array.z(2)->getVal() == 10,
                L"third element of array has an invalid value");
        }
    };
} // namespace FrameworkTests