/**
 * @file TestStatisticsBuffer.cpp
 * @date May 3, 2010
 * @author ron
 *
 * @brief
 */

#include "../TestData.h"
#include "../../TestBase/TestBase.h"

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

	/**
	 * @struct StatisticsBufferFixture
	 * @brief Helper struct providing set-up/tear-down of StatisticsBuffer tests
	 *
	 * @copydetails EventFixture
	 */
	struct StatisticsBufferFixture
	{
		SuiteBase::SimulationTest sim;
		TestLogConsumer::Ptr log;
		ProducerTest producer;
		ReportTest reporter;
		TypeInfo type;

		StatisticsBufferFixture()
		:	sim( "StatisticsBufferTestSim" )
		,	log( TestLogConsumer::create() )
		,	producer( sim, "StatisticsBufferTestFixtureProducer" )
		,	reporter()
		,	type( typeid(ProducerTest) )
		{
			sim.addConsumer( log );
		}
	};

	TEST_FIXTURE( StatisticsBufferFixture, Construction )
	{
		Label label = "StatisticsBufferTestConstruction";
		StatisticsBufferPtr buf = StatisticsBuffer::create( sim, label, true );
		CHECK( buf->isEmpty() );
		CHECK( buf->isBufferingUpdates() );
		CHECK_EQUAL( label, buf->getLabel() );

		label = "StatisticsBufferTestConstruction2";
		StatisticsBufferPtr buf2 = StatisticsBuffer::create( sim, label, false );
		CHECK( ! buf2->isBufferingUpdates() );

		label = "StatisticsBufferTestConstruction3";
		StatisticsBufferPtr buf3 = StatisticsBuffer::create( sim, label );
		CHECK( ! buf3->isBufferingUpdates() );
	}

	StatisticsBuffer::ProducerStatsPtr getStats( StatisticsBufferPtr buf,
			const data::Producer& sender )
	{
		StatisticsBuffer::StatisticsMap::const_iterator found =
				buf->getStatistics().find( sender.getLabel() );
		if( found != buf->getStatistics().end() )
		{
			return found->second;
		}
		else
		{
			return StatisticsBuffer::ProducerStatsPtr();
		}
	}

	TEST_FIXTURE( StatisticsBufferFixture, Consume )
	{
		StatisticsBufferPtr buf = StatisticsBuffer::create( sim, "StatisticsBufferTestConsume", true );
		sim.addConsumer( channel_id::statistics, buf );
		producer.statistics << producer.log( "Ignored Record" );
		CHECK( buf->isEmpty() );

		producer.statistics << producer.param( "Param", 1 );
		CHECK( ! buf->isEmpty() );
		CHECK_EQUAL( (std::size_t) 1, buf->getStatistics().size() );

		StatisticsBuffer::ProducerStatsPtr stats = getStats( buf, producer );
		CHECK( stats );
		CHECK( producer.getType() == stats->senderType );
		CHECK_EQUAL( (std::size_t) 1, stats->paramStats.size() );
		CHECK( stats->paramStats.find( "Param" ) != stats->paramStats.end() );

		producer.statistics << producer.count( "Counter", 1 );
		CHECK_EQUAL( (std::size_t) 1, stats->countStats.size() );
		CHECK( stats->countStats.find( "Counter" ) != stats->countStats.end() );

		producer.statistics << producer.update( "Update", 1 );
		CHECK_EQUAL( (std::size_t) 1, stats->updateStats.size() );
		const StatisticsBuffer::UpdateStats& updateStats = stats->updateStats.find( "Update" )->second;
		CHECK_EQUAL( (std::size_t) 1, updateStats.updates.size() );

		sim.setCurrentTime( 678 );
		producer.statistics << producer.reset();
		CHECK( ! stats->paramStats.empty() );
		CHECK( stats->countStats.empty() );
		CHECK( stats->updateStats.empty() );
		CHECK_EQUAL( 678, stats->resetTime );
	}

	TEST_FIXTURE( StatisticsBufferFixture, GetUpdates )
	{
		StatisticsBufferPtr buf = StatisticsBuffer::create( sim, "StatisticsBufferTestConsume", true );
		sim.addConsumer( channel_id::statistics, buf );

		// producer not found
		CHECK_THROW( buf->getUpdates( producer.getLabel(), "Property" ), odemx::DataException );

		sim.setCurrentTime( 345 );
		producer.statistics << producer.update( "Property", 101 );
		// producer exists, but property not found
		CHECK_THROW( buf->getUpdates( producer.getLabel(), "Unknown Property" ), odemx::DataException );

		// check updates
		sim.setCurrentTime( 380 );
		producer.statistics << producer.update( "Property", 102 );
		const StatisticsBuffer::UpdateDeque& updates =
				buf->getUpdates( producer.getLabel(), "Property" );
		CHECK_EQUAL( 345, updates.front().first );
		CHECK_EQUAL( 101, updates.front().second );
		CHECK_EQUAL( 380, updates.back().first );
		CHECK_EQUAL( 102, updates.back().second );
	}

	TEST_FIXTURE( StatisticsBufferFixture, Reset )
	{
		StatisticsBufferPtr buf = StatisticsBuffer::create( sim, "StatisticsBufferTestConsume", true );
		sim.addConsumer( channel_id::statistics, buf );

		producer.statistics << producer.param( "Param", 1 );
		producer.statistics << producer.count( "Counter", 1 );
		producer.statistics << producer.update( "Update", 1 );

		buf->reset( 768 );
		StatisticsBuffer::ProducerStatsPtr stats = getStats( buf, producer );
		CHECK_EQUAL( 768, stats->resetTime );
		CHECK( ! stats->paramStats.empty() );
		CHECK( stats->countStats.empty() );
		CHECK( stats->updateStats.empty() );
	}

	TEST_FIXTURE( StatisticsBufferFixture, Report )
	{
		StatisticsBufferPtr buf = StatisticsBuffer::create( sim, "StatisticsBufferTestConsume" );
		sim.addConsumer( channel_id::statistics, buf );

		producer.statistics << producer.param( "Param", 11 );
		producer.statistics << producer.count( "Counter", 12 );
		sim.setCurrentTime( 0 );
		producer.statistics << producer.update( "Update", 0 );
		sim.setCurrentTime( 1 );
		producer.statistics << producer.update( "Update", 25 );
		sim.setCurrentTime( 2 );
		producer.statistics << producer.update( "Update", 50 );
		StatisticsBuffer::ProducerStatsPtr stats = getStats( buf, producer );

		reporter.addReportProducer( *buf );
		reporter.generateReport();

		CHECK_EQUAL( producer.getLabel(), reporter.allTableContent[ 0 ][ 0 ] );
		CHECK_EQUAL( toString( stats->resetTime ), reporter.allTableContent[ 0 ][ 1 ] );
		CHECK_EQUAL( toString( 11 ), reporter.allTableContent[ 0 ][ 2 ] );
		CHECK_EQUAL( toString( 12 ), reporter.allTableContent[ 0 ][ 3 ] );
		CHECK_EQUAL( producer.getLabel(), reporter.allTableContent[ 1 ][ 0 ] );
		CHECK_EQUAL( "Update", reporter.allTableContent[ 1 ][ 1 ] );
		CHECK_EQUAL( toString( 3 ), reporter.allTableContent[ 1 ][ 2 ] );
		CHECK_EQUAL( toString( 0 ), reporter.allTableContent[ 1 ][ 3 ] );
		CHECK_EQUAL( toString( 50 ), reporter.allTableContent[ 1 ][ 4 ] );
		CHECK_EQUAL( toString( 25 ), reporter.allTableContent[ 1 ][ 5 ] );
		CHECK_EQUAL( toString( 12.5 ), reporter.allTableContent[ 1 ][ 6 ] );
		CHECK_EQUAL( toString( 20.4124 ), reporter.allTableContent[ 1 ][ 7 ] );
		CHECK_EQUAL( toString( 12.5 ), reporter.allTableContent[ 1 ][ 8 ] );

//		for( std::size_t i = 0; i < reporter.allTableContent.size(); ++i )
//		{
//			for( std::size_t j = 0; j < reporter.allTableContent[ i ].size(); ++j )
//			{
//				std::cout << "[" << i << ", " << j << "] " << reporter.allTableContent[ i ][ j ] << std::endl;
//			}
//		}
	}

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