/**
 * @file TestListInSort.cpp
 * @brief Tests for ODEMx list insertion function template
 */

#include "../Main/Globals.h"
#include <odemx/base/Comparators.h>
#include <odemx/base/DefaultSimulation.h>
#include <odemx/base/Event.h>
#include <odemx/base/Process.h>
#include <odemx/util/ListInSort.h>

using namespace std;
using namespace odemx::base;

/// @cond DOXYGEN_SKIP
SUITE( Util )
{
/// @endcond

	struct ListInSortEvent: public Event
	{
		ListInSortEvent( const data::Label& label )
		:	Event( getDefaultSimulation(), label )
		{}
		virtual void eventAction() {}
	};

	struct ListInSortProcess: public Process
	{
		ListInSortProcess( const data::Label& label )
		:	Process( getDefaultSimulation(), label )
		{}
		virtual int main() { return 0; }
	};

	/**
	 * @test odemx::ListInSort using \c std::less as comparison functor
	 *
	 * An integer list is filled using <tt>ListInSort< int, less< int > ></tt>.
	 * The inserted numbers are out of order, but the serialized output of the
	 * list is expected to be sorted from small to large numbers.
	 *
	 * ListInSort also returns an iterator to the position of the newly
	 * inserted item, which is checked by comparing it to the iterator
	 * returned by find.
	 */
	TEST( ListInSortOrder )
	{
		list< int > theList;
		list< int >::iterator iter;
		less< int > cmpLess;

		iter = ListInSort< int, less< int > >( theList, 1, cmpLess );
		CHECK( iter == find( theList.begin(), theList.end(), 1 ) );

		iter = ListInSort< int, less< int > >( theList, 5, cmpLess );
		CHECK( iter == find( theList.begin(), theList.end(), 5 ) );

		iter = ListInSort< int, less< int > >( theList, 3, cmpLess );
		CHECK( iter == find( theList.begin(), theList.end(), 3 ) );

		iter = ListInSort< int, less< int > >( theList, 4, cmpLess );
		CHECK( iter == find( theList.begin(), theList.end(), 4 ) );

		iter = ListInSort< int, less< int > >( theList, 2, cmpLess );
		CHECK( iter == find( theList.begin(), theList.end(), 2 ) );
		CHECK( theList.size() == 5 );

		ostringstream contents;
		for( iter = theList.begin(); iter != theList.end(); ++iter )
		{
			contents << *iter;
		}
		CHECK( contents.str() == "12345" );
	}

	/**
	 * @test odemx::ListInSort using \c odemx::DefaultCmp for priority
	 *
	 * The default comparison functor for odemx scheduling is of type
	 * DefaultCmp, which sorts Sched objects first by their execution time,
	 * and in case of equality by their priority. If both are equal, the
	 * order is decided by the value of the \c ListInSort parameter \c fifo.
	 * When set to \c true, the new object will be appended in the
	 * appropriate list position, the new object will be prepended.
	 *
	 * This test checks list insertion of Sched objects with the same
	 * execution time. Thus, the outcome is expected to be sorted by their
	 * priority.
	 */
	TEST( ListInSortDefaultCmpPriority )
	{
		list< Sched* > theList;
		list< Sched* >::iterator iter;

		ListInSortEvent cmpEvent0( "DefaultCmpPriorityEvent0" );
		ListInSortEvent cmpProcess1( "DefaultCmpPriorityProcess1" );
		ListInSortEvent cmpEvent2( "DefaultCmpPriorityEvent2" );

		CHECK_EQUAL( cmpEvent0. getExecutionTime(), cmpProcess1. getExecutionTime() );
		CHECK_EQUAL( cmpProcess1. getExecutionTime(), cmpEvent2. getExecutionTime() );

		// sort based upon priority
		cmpEvent0.setPriority( 0 );
		cmpProcess1.setPriority( 1 );
		cmpEvent2.setPriority( 2 );

		iter = ListInSort< Sched*, DefaultCmp >( theList, &cmpProcess1, defCmp );
		CHECK( theList.size() == 1 );
		CHECK( iter == find( theList.begin(), theList.end(), &cmpProcess1 ) );

		iter = ListInSort< Sched*, DefaultCmp >( theList, &cmpEvent2, defCmp );
		CHECK( theList.size() == 2 );
		CHECK( iter == find( theList.begin(), theList.end(), &cmpEvent2 ) );

		iter = ListInSort< Sched*, DefaultCmp >( theList, &cmpEvent0, defCmp );
		CHECK( theList.size() == 3 );
		CHECK( iter == find( theList.begin(), theList.end(), &cmpEvent0 ) );

		ostringstream contents;
		for( iter = theList.begin(); iter != theList.end(); ++iter )
		{
			contents << (*iter)->getLabel();
		}
		CHECK_EQUAL( cmpEvent2.getLabel() + cmpProcess1.getLabel() + cmpEvent0.getLabel(), contents.str() );
	}

	/**
	 * @test odemx::ListInSort LIFO insertion
	 *
	 * This test checks LIFO insertion by taking two Events which are
	 * equal to the comparison functor and checks insertion with \c fifo
	 * set to \c false. \c cmpEventLIFO is expected to be inserted before
	 * \c cmpEvent.
	 */
	TEST( ListInSortLifo )
	{
		list< Sched* > theList;
		list< Sched* >::iterator iter;

		ListInSortEvent cmpEvent( "--CompareEvent" );
		ListInSortEvent cmpEventLIFO( "CompareEventLIFO" );

		// check all test events are equal for defCmp
		CHECK( cmpEvent.getExecutionTime() == cmpEventLIFO.getExecutionTime() );
		CHECK( cmpEvent.getPriority() == cmpEventLIFO.getPriority() );

		iter = ListInSort< Sched*, DefaultCmp >( theList, &cmpEvent, defCmp );
		CHECK( theList.front() == &cmpEvent );
		CHECK( iter == find( theList.begin(), theList.end(), &cmpEvent ) );

		iter = ListInSort< Sched*, DefaultCmp >( theList, &cmpEventLIFO, defCmp, false );
		CHECK( theList.front() == &cmpEventLIFO );
		CHECK( iter == find( theList.begin(), theList.end(), &cmpEventLIFO ) );

		ostringstream contents;
		for( iter = theList.begin(); iter != theList.end(); ++iter )
		{
			contents << (*iter)->getLabel();
		}
		CHECK( contents.str() == "CompareEventLIFO--CompareEvent" );
	}

	/**
	 * @test odemx::ListInSort FIFO insertion
	 *
	 * This test checks FIFO insertion by taking two Events which are
	 * equal to the comparison functor and checks insertion with \c fifo
	 * set to \c false. \c cmpEventFIFO is expected to be inserted after
	 * \c cmpEvent.
	 */
	TEST( ListInSortFifo )
	{
		list< Sched* > theList;
		list< Sched* >::iterator iter;

		ListInSortEvent cmpEvent( "CompareEvent--" );
		ListInSortEvent cmpEventFIFO( "CompareEventFIFO" );

		// check all test events are equal for defCmp
		CHECK( cmpEvent.getExecutionTime() == cmpEventFIFO.getExecutionTime() );
		CHECK( cmpEvent.getPriority() == cmpEventFIFO.getPriority() );

		iter = ListInSort< Sched*, DefaultCmp >( theList, &cmpEvent, defCmp );
		CHECK( theList.front() == &cmpEvent );
		CHECK( iter == find( theList.begin(), theList.end(), &cmpEvent ) );

		iter = ListInSort< Sched*, DefaultCmp >( theList, &cmpEventFIFO, defCmp, true );
		CHECK( theList.back() == &cmpEventFIFO );
		CHECK( iter == find( theList.begin(), theList.end(), &cmpEventFIFO ) );

		ostringstream contents;
		for( iter = theList.begin(); iter != theList.end(); ++iter )
		{
			contents << (*iter)->getLabel();
		}
		CHECK( contents.str() == "CompareEvent--CompareEventFIFO" );
	}


	/**
	 * @test odemx::ListInSort using \c odemx::QPriorityCmp as comparison functor
	 *
	 * ODEMx provides another comparison functor of type QPriorityCmp, which
	 * sorts Process objects by their queue priority value. If compared objects
	 * are equal, the order is decided by the value of the \c ListInSort
	 * parameter \c fifo. When set to \c true, the new object will be appended
	 * in the appropriate list position, the new object will be prepended.
	 *
	 * This test checks list insertion of Process objects sorted by their
	 * queue priority.
	 */
	TEST( ListInSortQueuePriority )
	{
		list< Process* > theList;
		list< Process* >::iterator iter;

		ListInSortProcess cmpProcess( "-ComparisonProcess-" );
		ListInSortProcess cmpProcessHigh( "ComparisonProcessHigh" );
		ListInSortProcess cmpProcessLow( "ComparisonProcessLow" );

		cmpProcess.setQueuePriority( 2, false );
		cmpProcessHigh.setQueuePriority( cmpProcess.getQueuePriority() + 1, false );
		cmpProcessLow.setQueuePriority( cmpProcess.getQueuePriority() - 1, false );
		CHECK( cmpProcessLow.getQueuePriority() < cmpProcess.getQueuePriority() );
		CHECK( cmpProcess.getQueuePriority() < cmpProcessHigh.getQueuePriority() );

		iter = ListInSort< Process*, QPriorityCmp >( theList, &cmpProcess, qPrioCmp );
		CHECK( theList.front() == &cmpProcess );
		CHECK( iter == find( theList.begin(), theList.end(), &cmpProcess ) );

		iter = ListInSort< Process*, QPriorityCmp >( theList, &cmpProcessHigh, qPrioCmp );
		CHECK( theList.front() == &cmpProcessHigh );
		CHECK( iter == find( theList.begin(), theList.end(), &cmpProcessHigh ) );

		iter = ListInSort< Process*, QPriorityCmp >( theList, &cmpProcessLow, qPrioCmp );
		CHECK( theList.back() == &cmpProcessLow );
		CHECK( iter == find( theList.begin(), theList.end(), &cmpProcessLow ) );

		ostringstream contents;
		for( iter = theList.begin(); iter != theList.end(); ++iter )
		{
			contents << (*iter)->getLabel();
		}
		CHECK_EQUAL( cmpProcessHigh.getLabel() + cmpProcess.getLabel() + cmpProcessLow.getLabel(), contents.str() );
	}

/// @cond DOXYGEN_SKIP
}
/// @endcond
