/**
 * @file TestData.h
 * @date Apr 29, 2010
 * @author ron
 *
 * @brief
 */

#ifndef TESTDATA_H_
#define TESTDATA_H_

#ifdef ODEMX_USE_ODBC
	#undef ODEMX_USE_ODBC
	#ifndef CPPLOG_USE_SQLITE
	#define CPPLOG_USE_SQLITE
	#endif
#endif

#include <UnitTest++.h>
#include "../Main/Globals.h"

#include <odemx/data/SimRecordFilter.h>

#include <odemx/base/Simulation.h>
#include <odemx/data/LoggingManager.h>
#include <odemx/data/ManagedChannels.h>
#include <odemx/data/Observable.h>
#include <odemx/data/Producer.h>
#include <odemx/data/Report.h>
#include <odemx/data/ReportProducer.h>
#include <odemx/data/ReportTable.h>
#include <odemx/data/SimRecord.h>
#include <odemx/data/buffer/StatisticsBuffer.h>
#include <odemx/data/output/TimeFormat.h>
#include <odemx/data/output/GermanTime.h>
#include <odemx/data/output/Iso8601Time.h>
#include <odemx/data/output/DatabaseWriter.h>
#include <odemx/data/output/ErrorWriter.h>
#include <odemx/data/output/OStreamReport.h>
#include <odemx/data/output/OStreamWriter.h>
#include <odemx/data/output/XmlWriter.h>
#include <odemx/data/output/XmlReport.h>
#include <odemx/statistics/Count.h>
#include <odemx/util/Exceptions.h>

#include <Poco/File.h>

#include <fstream>
#include <sstream>

namespace SuiteData {

	using namespace odemx;
	using namespace odemx::data;
	using namespace odemx::data::output;
	using namespace odemx::data::buffer;

	/************************* OBSERVABLE *************************************/

	/**
	 * @class ObservableTestObserver
	 * @brief An observer class for testing template Observable
	 */
	template < typename T = int >
	class ObservableTestObserver:
		public ObserverBase
	{
	public:
		/**
		 * @brief Constructor
		 * @param output determines whether observations are sent to stdout
		 */
		ObservableTestObserver( bool output )
		:	ObserverBase( output )
			{}
		/// the method to be called by the event macro _obsForEach
		void onExecute( NamedElement* sender )
		{
			ObserverBase::saw( "onExecute", sender );
		}
		/// the method to be called by the attribute change macro _obsAForEach
		void onChangeMember( NamedElement* sender, T oldValue, T newValue )
		{
			ObserverBase::saw( "onChangeMember", sender, toString( oldValue ), toString( newValue ) );
		}
	};

	/**
	 * @class ObservableTest
	 * @brief A subclass for testing ODEMx observation macros
	 */
	class ObservableTest:
		public Observable< ObservableTestObserver<> >,
		public NamedElement
	{
	public:
		int member; ///< data member monitored for attribute change

		/// Constructor
		ObservableTest( base::Simulation& sim, const data::Label& l,
				int m, ObservableTestObserver<>* o )
		:	Observable< ObservableTestObserver<> >( o ),
			NamedElement( sim, l ),
			member( m )
			{}
		/// a method to test the observer event macro _obsForEach
		virtual void execute()
		{
			ODEMX_OBS( ObservableTestObserver<>, Execute( this ) );
		}
		/// a method to test the attribute change macro _obsAForEach
		void setMember( int newValue )
		{
			ODEMX_OBS_ATTR( ObservableTestObserver<>, Member, member, newValue );
			member = newValue;
		}
	};

	/**
	 * @class ObservableTestT
	 * @brief A templated subclass for testing ODEMx template observation macros
	 *
	 * @tparam T the type of the data member \c t_member
	 */
	template < typename T >
	class ObservableTestT:
		public Observable< ObservableTestObserver< T > >,
		public NamedElement
	{
	public:
		T t_member; ///< data member monitored for attribute change

		/// Constructor
		ObservableTestT( base::Simulation& sim, const data::Label& l,
				T t, ObservableTestObserver< T >* o )
		:	Observable< ObservableTestObserver< T > >( o ),
			NamedElement( sim, l ),
			t_member( t )
		{}
		/// a method to test the template observer event macro _obsForEachT
		virtual void execute()
		{
			ODEMX_OBS_T( ObservableTestObserver< T >, Execute( this ) );
		}
		/// a method to test the template attribute change macro _obsAForEachT
		void setMember( T newValue )
		{
			ODEMX_OBS_ATTR_T( ObservableTestObserver< T >, Member, t_member, newValue );
			t_member = newValue;
		}
	};

	/************************* PRODUCER ***************************************/

	class ProducerTest
	:	public Producer
	{
	public:
		ProducerTest( base::Simulation& sim, const Label& label )
		:	Producer( sim, label )
		{}

		using Producer::trace;
		using Producer::debug;
		using Producer::info;
		using Producer::warning;
		using Producer::error;
		using Producer::fatal;
		using Producer::statistics;

		using Producer::count;
		using Producer::param;
		using Producer::update;
		using Producer::reset;
		using Producer::log;
	};

	/************************* REPORT *****************************************/

	/**
	 * @class ReportProducerTest
	 * @brief A subclass for testing the ODEMx report mechanism
	 */
	class ReportProducerTest:
		public ReportProducer
	{
	public:
		bool sentReport; ///< monitors whether report() was called

		/// Constructor
		ReportProducerTest( base::Simulation& sim, const Label& label )
		:	ReportProducer( sim, label )
		,	sentReport( false )
		{}

		/**
		 * @brief implementation of pure virtual function, sets \c sentReport
		 * to \c true
		 * @param r the Report object to send the data to
		 */
		virtual void report( Report& r )
		{
			sentReport = true;
		}
	};

	/**
	 * @class ReportTest
	 * @brief A subclass for testing the ODEMx report mechanism
	 */
	class ReportTest:
		public Report
	{
	public:
		bool calledStartProcessing;
		bool calledEndProcessing;
		bool processedTables; ///< monitors whether processTables() was called
		std::vector< std::vector< std::string > > allTableContent;

		using Report::findTable;

		/// Constructor
		ReportTest()
		:	calledStartProcessing( false )
		,	calledEndProcessing( false )
		,	processedTables( false )
		{}
		/**
		 * @brief get a list of the associated ReportProducers
		 * @return a reference to the internal list
		 */
		std::set< ReportProducer* >& getReportProducers()
		{
			return Report::producers_;
		}
		/**
		 * @brief get a list of the created tables
		 * @return a reference to the internal list
		 */
		std::vector< ReportTable* >& getReportTables()
		{
			return Report::tables_;
		}
		/**
		 * @brief implementation of pure virtual function, sets \c
		 * processedTables to \c true
		 *
		 * Aside from testing odemx::Report, this function is also used for
		 * checking all ReportProducer::report() results
		 */
		virtual void processTables()
		{
			processedTables = true;

			ReportTable* tab = 0;
			std::vector< ReportTable* >::iterator it;

			// iterate over the tables of all ReportProducers
			for( it = tables_.begin(); it != tables_.end(); ++it )
			{
				std::vector< std::string > tableContent;

				tab = *it;

				if( tab == 0 )
				{
					continue;
				}

				for ( size_t line = 0; line < tab -> getNumberOfLines(); ++line )
				{
					for( size_t col = 0; col < tab -> getNumberOfColumns(); ++col )
					{
						tableContent.push_back( tab -> getSTRING( col, line ) );
					}
				}

				allTableContent.push_back( tableContent );
			}
		}
		/// Check if the start method is called during report generation
		virtual void startProcessing() { calledStartProcessing = true; };
		/// Check if the end method is called during report generation
		virtual void endProcessing() { calledEndProcessing = true; };
	};

	/************************** REPORT TABLE **********************************/

	/**
	 * @class ColumnTest
	 * @brief A subclass for testing ODEMx table columns
	 */
	template < typename DataType >
	class ColumnTest:
		public ReportTable::ColumnT< DataType >
	{
	public:
		/// Constructor
		ColumnTest( const data::Label& l, ReportTable::Column::Type t )
		:	ReportTable::ColumnT< DataType >( l, t )
		{}
		/// append data to column, i.e. add content of next line in table
		void addData( DataType d ) { ReportTable::ColumnT< DataType >::addData( d ); }
		/// read data from column, the given line is range-checked
		DataType getData( std::size_t line ) { return ReportTable::ColumnT< DataType >::getData( line ); }
	};

	/************************** TIME FORMAT ***********************************/

	/**
	 * @class TimeFormatTest
	 * @brief Helper class for testing Time::Format, implements a pure virtual
	 * function
	 */
	class TimeFormatTest:
		public TimeFormat
	{
	public:
		/// Default construction
		TimeFormatTest(): TimeFormat() {}

		/// Construction with Time::Base
		TimeFormatTest( TimeBase tb ): TimeFormat( tb ) {}

		/// implementation of pure virtual function, returns strings for testing
		virtual const std::string makeString() const
		{
			std::ostringstream timeStream;
			timeStream
			<< "time: "
			<< year << "/" << month << "/" << day << "-"
			<< hour << ':' << min << ':' << sec;
			return timeStream.str();
		}
	};

} // namespace SuiteData

#endif /* TESTDATA_H_ */
