#include "Test.h"

#include "Array.h"
#include "Log.h"
#include <algorithm>

namespace MM
{

class Array_test : public Test
{
public:
    Array_test (const Text & suite_name) : Test (suite_name) { }

    void run()
    {
        test_int();                                 count_tests();
        test_class_without_default_constructor();   count_tests();
        test_Out_of_range_exception();              count_tests();
        //test_add_at();                              count_tests();//fix empty
        test_reserve();                             count_tests();
        test_operators_plus_equal();                count_tests();
        test_STL();                                 count_tests();
        test_erase();                               count_tests();
        test_find();                                count_tests();
        test_remove_at();                           count_tests();
        test_array_of_array();                      count_tests();
//*/
    }

    void test_int();
    void test_class_without_default_constructor();
    void test_Out_of_range_exception();
    void test_add_at();
    void test_reserve();
    void test_operators_plus_equal();
    void test_STL();
    void test_erase();
    void test_find();
    void test_remove_at();
    void test_array_of_array();


    class Class_without_default_constructor
    {
        Own <int>       v_;
        bool            was_constructed_;
        bool            was_destroyed_;
        static int      constructor_calls_;
        static int      destructor_calls_;

    public:
        int             value()                 {return v_();}
        static int      constructor_calls()     {return constructor_calls_;}
        static int      destructor_calls ()     {return destructor_calls_; }
        bool            was_constructed  ()     {return was_constructed_;  }
        bool            was_destroyed    ()     {return was_destroyed_;    }

        Class_without_default_constructor (int v) 
        : 
            v_(new int), was_constructed_(true), was_destroyed_(false)
        { 
            v_() = v; 
            ++constructor_calls_;
        }

        ~Class_without_default_constructor ()
        { 
            ++destructor_calls_; 
            was_destroyed_ = true;
        }

        Class_without_default_constructor 
            (const Class_without_default_constructor & r)
        : 
            v_(new int), was_constructed_(true), was_destroyed_(false)
        {
            v_() = r.v_();
            ++constructor_calls_;   
        }

        const Class_without_default_constructor & operator = 
            (const Class_without_default_constructor & r)
        {
            if (&r != this)
                v_() = r.v_();
            return *this;
        }
    };
};

Array_test test_array ("correctness");

int  Array_test::Class_without_default_constructor::constructor_calls_ = 0;
int  Array_test::Class_without_default_constructor::destructor_calls_  = 0;

void Array_test::
test_int()
{
    Array <int> a; TEST ("", a.size() == 0);   //TEST ("", a.capacity() == 0);
    a.push_back (0);   TEST ("", a.size() == 1);   //TEST ("", a.capacity() == 1);
    a.push_back (1);   TEST ("", a.size() == 2);   //TEST ("", a.capacity() == 2);
    a.push_back (2);   TEST ("", a.size() == 3);   //TEST ("", a.capacity() == 4);
    a.push_back (3);   TEST ("", a.size() == 4);   //TEST ("", a.capacity() == 4);
    a.push_back (4);   TEST ("", a.size() == 5);   //TEST ("", a.capacity() == 8);
    a.push_back (5);   TEST ("", a.size() == 6);   //TEST ("", a.capacity() == 8);
    a.push_back (6);   TEST ("", a.size() == 7);   //TEST ("", a.capacity() == 8);
    a.push_back (7);   TEST ("", a.size() == 8);   //TEST ("", a.capacity() == 8);
    a.push_back (8);   TEST ("", a.size() == 9);   //TEST ("", a.capacity() ==16);

    for (int i=0;  i<a.size();  ++i)
        TEST ("Array of ints has correct values.", a[i] == i);
}

void Array_test::
test_class_without_default_constructor ()
{
    {
        Array <Class_without_default_constructor> a;
        a.push_back (Class_without_default_constructor (0));
        a.push_back (Class_without_default_constructor (1));
        a.push_back (Class_without_default_constructor (2));
        a.push_back (Class_without_default_constructor (3));

        TEST ("Array size == 4", a.size() == 4);

        for (int i=0;  i<a.size();  ++i)
        {
            TEST ("Array has correct values.", a[i].value() == i);
            TEST ("", a[i].was_constructed());
        }
    }
/*
    log() << "\n";
    log() << Text(Class_without_default_constructor::constructor_calls());
    log() << "\n";
    log() << Text(Class_without_default_constructor::destructor_calls());
    log() << "\n";
    //*/

    TEST ("Number of constructor calls is equal to number of destructor calls ", 
        Class_without_default_constructor::constructor_calls() == 
        Class_without_default_constructor::destructor_calls ());
}

void Array_test::
test_Out_of_range_exception () //fix essential property
{
    /*#ifndef NDEBUG
    log().set_on (false);
    Array <int> a;
    a.push_back (0);    a.push_back (1);    a.push_back (2);
    a[0];               a[1];               a[2];

    bool success = false;
    try
    {
        a[3];
        FAIL ("This point should be unreachable");
    }
    catch(Flaw &)
    {
        success = true;
    }
    TEST ("array[3] is out of range", success);

    success = false;
    try
    {
        a[-1];
        FAIL ("This point should be unreachable");
    }
    catch(Flaw &)
    {
        success = true;
    }
    TEST ("array[-1] is out of range", success);
    log().set_on (true);

    #else
    TEST ("No testing", true);
    #endif//*/
}

void Array_test::
test_add_at ()
{
    /*Array< int > a;
    a.add( 1 );    
    a.add( 3 );    
    a.add_at( 0, 0 );
    a.add_at( 2, 2 );
    a.add_at( 4, 4 );
    TEST( "array size == 5", a.size() == 5 );

    for( int i=0;  i<a.size();  ++i )
    {
        //logger() << a[i] << "\n";
        TEST("", a[i] == i );
    }

    a.detach( 4 );
    a.detach( 2 );
    a.detach( 0 );
    TEST("", a.size() == 2 && a[0] == 1 && a[1] == 3 );//*/
}

void Array_test::
test_reserve ()
{
    /*Array <int> a (7);
    TEST ("", a.size() == 0 && a.capacity() == 7);
    a.push_back (0);    
    a.push_back (1);    
    TEST ("", a.size() == 2 && a.capacity() == 7);
    a.push_back (2);    
    a.push_back (3);    
    a.push_back (4);    
    a.push_back (5);    
    a.push_back (6);    
    a.push_back (7);    
    TEST ("", a.size() == 8 && a.capacity() == 14);
    a.reserve (7);
    TEST ("", a.size() == 8 && a.capacity() == 21);
    a.cancel_reserve();
    TEST ("", a.size() == 8 && a.capacity() == 8);
    a.clear();
    TEST ("", a.size() == 0 && a.capacity() == 8);
    a.cancel_reserve();
    TEST ("", a.size() == 0 && a.capacity() == 0);//*/
}

void Array_test::
test_operators_plus_equal ()
{
    /*int i;

    Array <int> a1 (7), a2 (7);
    a1.add (0);
    a1.add (1);
    a2.add (2);
    a2.add (3);
    a2.add (4);
    a1 += a2;
    TEST ("", a1.size()     == 5 && a2.size()     == 3);
    TEST ("", a1.capacity() == 5 && a2.capacity() == 7);
    for (i=0;  i<a1.size();  ++i)
        TEST ("", a1[i] == i);

    Array <int> a3( a1 );
    Array <int> a4 = a1;
    Array <int> a5;
    a5 = a1;
    TEST ("", a3.capacity() == 5 && a4.capacity() == 5 && a5.capacity() == 5);
    TEST ("", a3.size()     == 5 && a4.size()     == 5 && a5.size()     == 5);

    for (i=0;  i<a1.size();  ++i)
        TEST ("", a3[i] == i && a4[i] == i && a5[i] == i);//*/
}

void Array_test::
test_STL ()
{
    int i;

    Array <int> a;
    a.push_back (0);
    a.push_back (2);
    a.push_back (4);
    a.push_back (3);
    a.push_back (1);

    std::sort (a.begin(), a.end());

    for (i=0;  i<a.size();  ++i)
        TEST ("", a[i] == i);

    Array <int> a_copy;
    std::copy (a.begin(), a.end(), std::back_inserter (a_copy));
    TEST ("", a_copy.size() == a.size());
    for (i=0;  i<a.size();  ++i)
        TEST ("", a[i] == i);
}

void Array_test::
test_erase ()
{
//    Array <int> a (7);
    Array <int> a;
    a.push_back (0);     a.push_back (1);    a.push_back (2);    a.push_back (3);    
    a.push_back (4);     a.push_back (5);    a.push_back (6);    a.push_back (7);    

    TEST ("", a.size() == 8);
    //TEST ("", a.capacity() == 14);
    a.erase (a.begin()+3, a.begin()+6);

    TEST ("", a.size() == 5);
    //TEST ("", a.capacity() == 14);
    TEST ("", a[0]==0 && a[1]==1 && a[2]==2 && a[3]==6 && a[4]==7);

    a.clear ();
    TEST ("", a.size() == 0);
}

void Array_test::
test_find ()
{
    /*log().set_on (false);
    Array <int> a;
    a.push_back (0);     a.push_back (1);    a.push_back (2);    a.push_back (3);
    a.push_back (4);     a.push_back (5);    a.push_back (6);    a.push_back (7);
    TEST ("", a.find (5) == 5);

    bool success = false;
    try
    {
        a.find (55);
        FAIL ("This point should be unreachable");
    }
    catch(Flaw &)
    {
        success = true;
    }
    TEST ("array has not item 55", success);
    log().set_on (true);//*/
}

void Array_test::
test_remove_at ()
{
    /*Array <int> a (7);
    a.add (0);     a.add (1);    a.add (2);    a.add (3);    
    a.add (4);     a.add (5);    a.add (6);    a.add (7);    

    TEST ("", a.size() == 8 && a.capacity() == 14);
    a.remove_at (4);
    a.remove_at (a.find(3));
    a.remove_at (a.find(5));

    TEST ("", a.size() == 5 && a.capacity() == 14);
    TEST ("", a[0]==0 && a[1]==1 && a[2]==2 && a[3]==6 && a[4]==7);//*/
}

void Array_test::
test_array_of_array ()
{
    Array <Array <int> > a;
    a.push_back (Array <int>());
    a.push_back (Array <int>());
    a[0].push_back (1000);
    a[0].push_back (1001);
    a[1].push_back (1010);

    TEST ("", a[0][0] == 1000);
    TEST ("", a[0][1] == 1001);
    TEST ("", a[1][0] == 1010);
}

}//MM


