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


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

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

	/**
	 * @struct DatabaseWriterFixture
	 * @brief Helper struct providing set-up/tear-down of DatabaseWriter tests
	 *
	 * @copydetails EventFixture
	 */
	struct DatabaseWriterFixture
	{
		SuiteBase::SimulationTest sim;
		TestLogConsumer::Ptr log;
		ProducerTest producer;
		std::string dbFileName;
		TypeInfo type;

		DatabaseWriterFixture()
		:	sim( "DatabaseWriterTestSim" )
		,	log( TestLogConsumer::create() )
		,	producer( sim, "DatabaseWriterTestFixtureProducer" )
		,	dbFileName( "DatabaseWriterTest.db" )
		,	type( typeid(ProducerTest) )
		{
			Poco::File file( dbFileName );
			if( file.exists() )
			{
				file.remove();
			}

			sim.addConsumer( log );
		}
	};

	TEST_FIXTURE( DatabaseWriterFixture, Construction )
	{
		{
			DatabaseWriterPtr dbWriter = DatabaseWriter::create( dbFileName, 1 );
		}
		CHECK( Poco::File( dbFileName ).exists() );
	}

	TEST_FIXTURE( DatabaseWriterFixture, Consume )
	{
		DatabaseWriterPtr dbWriter = DatabaseWriter::create( dbFileName, 2 );
		sim.addConsumer( channel_id::info, dbWriter );

		StringLiteral msg = "DatabaseWriterTestConsume";
		StringLiteral detName = "DetailConsume";
		int detVal = 98765;

		// record gets stored in buffer
		producer.info << producer.log( msg ).detail( detName, detVal ).scope( type );
		CHECK_EQUAL( (std::size_t) 1, dbWriter->getBuffer().getSize() );

		const SimRecordBuffer::StoredRecord& rec = dbWriter->getBuffer().getStorage().front();
		CHECK_EQUAL( msg, rec.getText() );
		CHECK( rec.hasDetails() );
		CHECK_EQUAL( detName, rec.getDetails().front().first );
		CHECK_EQUAL( detVal, rec.getDetails().front().second.convert<int>() );
	}

	TEST_FIXTURE( DatabaseWriterFixture, Flush )
	{
		base::SimTime time = 2468;
		StringLiteral msg = "DatabaseWriterTestFlush";
		StringLiteral detName = "DetailFlush";
		int detVal = 43210;
		{
			DatabaseWriterPtr dbWriter = DatabaseWriter::create( dbFileName, 10 );
			sim.addConsumer( channel_id::info, dbWriter );
			sim.setCurrentTime( time );

			producer.info << producer.log( msg ).detail( detName, detVal ).scope( type );
			producer.info << producer.log( msg ).detail( detName, detVal ).scope( type );
			dbWriter->flush();

			CHECK_EQUAL( (std::size_t) 0, dbWriter->getBuffer().getSize() );
		}
		using namespace Poco::Data;
		using namespace Poco::Data::Keywords;
		Session session( "SQLite", dbFileName );
		CHECK( session.isConnected() );

		Statement stmt(session);
		int rowCount;
		stmt << "select count(*) from odemx_simulation_run", into( rowCount );
		stmt.execute();
		CHECK_EQUAL( 1, rowCount );

		stmt.reset( session );
		stmt << "select count(*) from odemx_sim_record", into( rowCount );
		stmt.execute();
		CHECK_EQUAL( 2, rowCount );

		stmt.reset( session );
		stmt << "select count(*) from odemx_sim_record_detail", into( rowCount );
		stmt.execute();
		CHECK_EQUAL( 2, rowCount );

		std::string str;
		stmt.reset( session );
		stmt << "select label from odemx_simulation_run where id = 1", into( str );
		stmt.execute();
		CHECK_EQUAL( sim.getLabel(), str );

		str.clear();
		stmt.reset( session );
		stmt << "select channel from odemx_sim_record where id = 1", into( str );
		stmt.execute();
		CHECK_EQUAL( channel_id::toString( channel_id::info ), str );

		str.clear();
		stmt.reset( session );
		stmt << "select text from odemx_sim_record where id = 1", into( str );
		stmt.execute();
		CHECK_EQUAL( std::string( msg.c_str() ) , str );

		str.clear();
		stmt.reset( session );
		stmt << "select class_scope from odemx_sim_record where id = 1", into( str );
		stmt.execute();
		CHECK_EQUAL( type.toString(), str );

		str.clear();
		stmt.reset( session );
		stmt << "select sender_label from odemx_sim_record where id = 1", into( str );
		stmt.execute();
		CHECK_EQUAL( producer.getLabel(), str );

		str.clear();
		stmt.reset( session );
		stmt << "select sender_type from odemx_sim_record where id = 1", into( str );
		stmt.execute();
		CHECK_EQUAL( producer.getType().toString(), str );

		str.clear();
		stmt.reset( session );
		stmt << "select sim_time from odemx_sim_record where id = 1", into( str );
		stmt.execute();
		CHECK_EQUAL( toString( time ), str );

		int simId;
		stmt.reset( session );
		stmt << "select sim_id from odemx_sim_record where id = 1", into( simId );
		stmt.execute();
		CHECK_EQUAL( 1, simId );

		str.clear();
		stmt.reset( session );
		stmt << "select name from odemx_sim_record_detail where id = 2", into( str );
		stmt.execute();
		CHECK_EQUAL( detName.c_str(), str );

		str.clear();
		stmt.reset( session );
		stmt << "select value from odemx_sim_record_detail where id = 2", into( str );
		stmt.execute();
		CHECK_EQUAL( toString( detVal ), str );

		int recId;
		stmt.reset( session );
		stmt << "select record_id from odemx_sim_record_detail where id = 2", into( recId );
		stmt.execute();
		CHECK_EQUAL( 2, recId );
	}

	TEST_FIXTURE( DatabaseWriterFixture, SetTimeFormat )
	{
		base::SimTime time = 2468;
		StringLiteral msg = "DatabaseWriterTestSetTimeFormat";
		GermanTime format( TimeBase( 2010, 5, 3, 19*60+5, TimeUnit::minutes ) );
		{
			DatabaseWriterPtr dbWriter = DatabaseWriter::create( dbFileName, 10 );
			sim.addConsumer( channel_id::info, dbWriter );
			sim.setCurrentTime( time );
			dbWriter->setTimeFormat( new GermanTime( format ) );

			// buffer gets flushed automatically when full
			producer.info << producer.log( msg );
			dbWriter->flush();
		}
		using namespace Poco::Data;
		using namespace Poco::Data::Keywords;
		Session session( "SQLite", dbFileName );
		CHECK( session.isConnected() );

		Statement stmt(session);
		std::string formattedTime;
		stmt << "select sim_time from odemx_sim_record where id = 1", into( formattedTime );
		stmt.execute();
		CHECK_EQUAL( format.timeToString( time ), formattedTime );
	}

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