#include "pch.h"
#include "CppUnitTest.h"
#include <Array.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 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 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() 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 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 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 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 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() 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" );
            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 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" );
        }
    };
}