//------------------------------------------------------------------------------
//	Copyright (C) 2009, 2010 Humboldt-Universitaet zu Berlin
//
//	This library is free software; you can redistribute it and/or
//	modify it under the terms of the GNU Lesser General Public
//	License as published by the Free Software Foundation; either
//	version 2.1 of the License, or (at your option) any later version.
//
//	This library is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//	Lesser General Public License for more details.
//
//	You should have received a copy of the GNU Lesser General Public
//	License along with this library; if not, write to the Free Software
//	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/**
 * @file StatisticsBuffer.h
 * @author Ronald Kluth
 * @date created at 2009/04/07
 * @brief Declaration of class odemx::data::buffer::StatisticsBuffer
 * @sa StatisticsBuffer.cpp
 * @since 3.0
 */

#ifndef ODEMX_DATA_BUFFER_STATISTICSBUFFER_INCLUDED
#define ODEMX_DATA_BUFFER_STATISTICSBUFFER_INCLUDED

#include <odemx/data/SimRecord.h>
#include <odemx/data/ReportProducer.h>
#include <odemx/statistics/Accumulate.h>

#include <CppLog/Consumer.h>

#ifdef _MSC_VER
#include <memory>
#else
#include <tr1/memory>
#endif

#include <deque>
#include <map>
#include <string>
#include <utility>

//----------------------------------------------------------forward declarations

namespace odemx {

namespace base { class Simulation; }

namespace data {
namespace buffer {

//-----------------------------------------------------------header declarations

/**
 * @brief A log consumer class that computes and buffers simulation statistics
 * @ingroup data
 * @author Ronald Kluth
 * @since 3.0
 * @see odemx::data::Producer odemx::data::LoggingManager
 *
 * In previous ODEMx versions, each @c ReportProducer stored its own
 * accumulated statistical data. With database connectivity, ODEMx needed
 * a concept that would allow the logging of each statistically relevant
 * piece of data. Hence, ODEMx offers the log channel statistics and several
 * convenience methods to create statistical log records. However, retaining
 * the previous functionality where accumulated data can be computed without
 * accessing a database, now requires a special log consumer class.
 *
 * The StatisticsBuffer consumes log records sent via channel @c statistics.
 * Producers create such log records in a specific format (namely counters,
 * parameters, updates, or resets) so that this consumer class can compute
 * accumulated data from them. The buffer can also store all updates in memory
 * so that it can be used later in the simulation programm. One such use is the
 * computation of a histogram, for example.
 *
 * Besices the log consumer interface, this class also implements the report
 * producer interface, meaning that it can directly be registered with a
 * Report object in order to produce table-based statistics reports. This
 * capability is also used when default logging is active.
 */
class StatisticsBuffer
:	public Log::Consumer< SimRecord >
,	public ReportProducer
{
public:

	/// Deque type for storing property updates, i.e. pairs of SimTime and values
	typedef std::deque< std::pair< base::SimTime, double > > UpdateDeque;

	/// Structure that holds value updates and the accumulated data
	struct UpdateStats
	{
		UpdateStats( base::Simulation& sim, const data::Label& accumLabel )
		:	accumulate( sim, accumLabel ) {}
		statistics::Accumulate accumulate;
		UpdateDeque updates;
	};

	/// Map type to associate parameter names and values
	typedef std::map< StringLiteral, DynamicVar > ParamMap;
	/// Map type to associate counted properties with counter values
	typedef std::map< StringLiteral, std::size_t > CountMap;
	/// Map type to associate updated properties with their statistical data
	typedef std::map< StringLiteral, UpdateStats > UpdateMap;

	/**
	 * @brief Stores all statistical data of one statistics producer
	 *
	 * A @c Producer logs parameters, counters, and property updates,
	 * all of which can be stored by an object of this class. A
	 * @c ProducerStats object will be created for each statistics-producing
	 * object in a simulation.
	 */
	struct ProducerStats
	{
		base::SimTime resetTime; ///< last reset time
		data::TypeInfo senderType; ///< sender type
		ParamMap paramStats; ///< parameters
		CountMap countStats; ///< counters
		UpdateMap updateStats; ///< updates
	};

	/// Pointer type to manage @c ProducerStats
	typedef std::tr1::shared_ptr< ProducerStats > ProducerStatsPtr;

	/// Buffer type for statistical data of many statistics producers
	typedef std::map< Label, ProducerStatsPtr > StatisticsMap;

	/// Creation via static method to enforce shared_ptr use
	static std::tr1::shared_ptr< StatisticsBuffer > create(
			base::Simulation& sim, const Label& label, bool bufferUpdates = false );

	/// Destruction
	virtual ~StatisticsBuffer();

	/// Check if the buffer contains any data
	bool isEmpty() const;

	/// Get read access to the internal statistics storage
	const StatisticsMap& getStatistics() const;

	/// Check whether update values are stored in a buffer
	bool isBufferingUpdates() const;

	/// Get read access to the stored values of a producer's statistics property
	const UpdateDeque& getUpdates( const Label& producer,
			const StringLiteral& property ) const;

	/// Reset accumulated data (counters, updates) of all statistics producers
	void reset( base::SimTime resetTime );

private:
	/// Stores all statistics computed from simulation components
	StatisticsMap statistics_;
	/// Determines whether update values are buffered or not
	bool bufferUpdates_;

private:
	/// Construction private, use create instead
	StatisticsBuffer( base::Simulation& sim, const Label& label, bool bufferUpdates );

	/// Implementation of ReportProducer interface
	virtual void report( data::Report& report );

	/// Implementation of log consumer interface, ignores all channels except @c statistics
	virtual void consume( const Log::ChannelId channelId, const SimRecord& record );

	/// Check for existing ProducerStats or create and add a new stats object
	ProducerStatsPtr getObjectStats( const data::Producer& sender );

	/// Non-copyable
	StatisticsBuffer( const StatisticsBuffer& );
	/// Non-assignable
	StatisticsBuffer& operator=( const StatisticsBuffer& );
};

/// Smart pointer type to manage StatisticsBuffer objects
typedef std::tr1::shared_ptr< StatisticsBuffer > StatisticsBufferPtr;

} } } // namespace odemx::data::buffer

#endif /* ODEMX_DATA_BUFFER_STATISTICSBUFFER_INCLUDED */
