#include "Test_suite.h"

#include "Test.h"
#include "Text_output.h"
#include "Std_text_output.h"
#include "Log.h"

#include <stdlib.h>     //fix for BCB6
#include <typeinfo>
#include <algorithm>

#include <string.h>

namespace MM
{

Text_output *& Test_suite::
default_output_ptr ()
{
    static Text_output *instance = 0;
    return instance;
}

Test_suite::
Test_suite (const Text& name)
: 
    out_(0), name_(name)
{
}

Test_suite::
Test_suite (const Text& name, Text_output& output)
: 
    out_( &output ), name_( name )
{
}

void Test_suite::
add_test (Test& new_test)
{
    new_test.set_output (*out_);
    tests_.push_back    (&new_test);
}

void Test_suite::
add_suite (Test_suite &new_suite)
{
    for (int i = 0;  i < new_suite.tests_.size();  ++i)
        add_test (*new_suite.tests_[i]);
}

void Test_suite::
run ()
{
    REQUIRE ("Output was initialized", out_ != 0);

    log() 
             << "\n"
             << "-----------------------------------------------------\n"
             << "            Test suite " << "\"" << name_.c_str() << "\"\n"
             << "-----------------------------------------------------\n";

    for (int i = 0;  i < tests_.size();  ++i)
    {
        CHECK ("Test [i] exist", tests_[i] != 0);
        tests_[i]->run_test();
    }

    log() << "\n";

    out_->line (Text("====>   ") + failed() + " failures " + " ("   + name_ + ")");
}


namespace
{
bool tests_equal (Test* t1, Test* t2)
{
    return typeid(*t1).name() == typeid(*t2).name();
}

bool test1_less_then_test2 (Test* t1, Test* t2)
{
    return strcmp (typeid(*t1).name(), typeid(*t2).name()) < 0;
}
}//namespace

void Test_suite::
sort ()
{
    std::sort  (tests_.begin(), tests_.end(), test1_less_then_test2);

    Array <Test*>::iterator new_end =
        std::unique (tests_.begin(), tests_.end(), tests_equal);

    tests_.erase (new_end, tests_.end());

    log() << "sorted\n";
}

void Test_suite::
mix ()
{
    std::random_shuffle (tests_.begin(), tests_.end());
    log() << "mixed\n";
}

int Test_suite::
passed () const
{
    int totPass = 0;
    for (int i = 0;  i < tests_.size();  ++i)
    {
        CHECK ("Test [i] exist", tests_[i] != 0);
        totPass += tests_[i]->passed();
    }
    return totPass;
}

int Test_suite::
failed () const
{
    int totFail = 0;
    for (int i = 0;  i < tests_.size();  ++i)
    {
        CHECK ("Test [i] exist", tests_[i] != 0);
        totFail += tests_[i]->failed();
    }
    return totFail;
}

void Test_suite::
run_all ()
{
    for (int i=0;  i<suites().size();  ++i)
        suites()[i]().run();
}

Test_suite & Test_suite::
singleton (const Text& suite_name)
{
    if (suites().size() == 0)
    {
        register_suite ("correctness");
        register_suite ("performance");
        register_suite ("demo");
        register_suite ("temporary");
    }

    if (!exist_suite (suite_name))    
        FLAW (Text("\"") + suite_name + "\" unit test suite does not exist.");

    int n=0;
    for (int i=0;  i<suites().size();  ++i)
    {
        if (suites()[i]().name_ == suite_name)
        {
            n = i;
            break;
        }
    }
            
    return suites()[n]();
}

void Test_suite::
register_suite (const Text& suite_name)
{
    CONTRACT REQUIRE ("Suite does not defined yet", 
        !exist_suite (suite_name));

    suites().push_back (Own <Test_suite>
        (new Test_suite (suite_name, default_output())));

    CONTRACT ENSURE ("Suite defined", exist_suite (suite_name));
}

bool Test_suite::
exist_suite (const Text& suite_name)
{
    for (int i=0;  i<suites().size();  ++i )
        if (suites()[i]().name_ == suite_name )
            return true;

    return false;
}

void Test_suite::
register_default_output (Text_output & output)
{
    default_output_ptr() = &output;
}

Text_output & Test_suite::
default_output ()
{
//    static File_text_output default_output( "_test_suite_output.txt" );
//    static Std_text_output default_output;
    static Default_output default_output;

    if( default_output_ptr() == 0 )  
//        default_output_ = &logger();
        default_output_ptr() = &default_output;

    return *default_output_ptr();
}

Array <Own <Test_suite> > & Test_suite::
suites ()
{
    static Array <Own <Test_suite> > singleton;
    return singleton;
}

}//MM

