//------------------------------------------------------------------------------
//	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 SimRecord.h
 * @author Ronald Kluth
 * @date created at 2009/03/16
 * @brief Declaration of class odemx::data::SimRecord
 * @sa SimRecord.cpp
 * @since 3.0
 */

#ifndef ODEMX_DATA_SIMRECORD_INCLUDED
#define ODEMX_DATA_SIMRECORD_INCLUDED

#include <odemx/base/SimTime.h>
#include <odemx/data/TableKey.h>
#include <odemx/data/TypeInfo.h>
#include <odemx/data/Label.h>
#include <odemx/util/StringConversion.h>
#include <CppLog/Record.h>
#include <CppLog/DeclareInfoType.h>
#include <Poco/Dynamic/Var.h>

#include <vector>
#include <utility>

namespace odemx {

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

namespace base { class Simulation; }

namespace data {

class Producer;

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

/// Variable type that can hold and convert many different types
typedef Poco::Dynamic::Var DynamicVar;
/// String literal wrapper type
typedef Log::StringLiteral StringLiteral;

/**
 * @brief Record type used for logging ODEMx simulation data
 * @ingroup data
 * @author Ronald Kluth
 * @since 3.0
 * @see odemx::data::Producer
 *
 * Starting with Version 3.0 ODEMx is based on a separate logging library
 * to provide for tracing, debug and error output. The record type of the
 * logging components is configurable. With this class, ODEMx provides its
 * own class type for logging information.
 *
 * SimRecord objects usually describe one notable simulation event, which is
 * given as a short text message. Other than that, objects of this type always
 * transport information about the sender, the simulation context and the
 * simulation time. Optionally, a class scope can be attached, and an arbitrary
 * number of details can be provided in the form of name-value-pairs.
 *
 * Usage within a producer class:
 * @code
 *   channel << log( "some event" ).detail( "name", value ).scope( typeid(ClassName) )
 * @endcode
 *
 * The channel is usually one of the seven log channels provided by ODEMx
 * (see odemx::data::LoggingManager). The method log creates a SimRecord object
 * with the aformentioned data, and the methods detail and scope can be chained
 * together in order to add more information to the log record.
 */
class SimRecord
{
public:
	/// Vector type for pairs of names and values of various types
	typedef std::vector< std::pair< StringLiteral, DynamicVar > > DetailVec;

	/**
	 * @brief Construction
	 * @param sim The simulation context
	 * @param sender The sender of the log record
	 * @param text A description of the simulation event
	 *
	 * SimRecord objects are meant to be created within classes derived from
	 * odemx::data::Producer. Therefore, these log records are usually not
	 * constructed directly because the class Producer provides several
	 * convenience methods to deal with the task.
	 */
	SimRecord( const base::Simulation& sim, const Producer& sender,
			const StringLiteral& text );

	/// Destruction
	virtual ~SimRecord();

	///	Get the text message of the record
	const StringLiteral& getText() const;
	/// Get the simulation context the record originated from
	const base::Simulation& getSimulation() const;
	/// Get a reference to the sender object of the record
	const Producer& getSender() const;
	/// Get the simulation time at which the record was created
	base::SimTime getTime() const;
	/// Get the class scope from which the record was sent
	const TypeInfo& getScope() const;
	/// Get a vector containing additional details as name-value pairs
	const DetailVec& getDetails() const;
	/// Get a @c shared_ptr referencing the detail vector
	const std::tr1::shared_ptr< DetailVec > getDetailPointer() const;

	/// Check whether the class scope of the record is set
	bool hasScope() const;
	/// Store the class scope from which this record was sent
	SimRecord& scope( const TypeInfo& type );

	/// Check whether the record carries additional details
	bool hasDetails() const;
	/// Add a detail pair consisting of a name and a value
	template < typename ValueT >
	SimRecord& detail( const StringLiteral& name, const ValueT& value )
	{
		if( ! details_ )
		{
			initDetailVec();
		}
		details_->push_back( DetailVec::value_type( name, value ) );
		return *this;
	}
	/// Store two details at once, i.e. pairs for the old and the new value
	template< typename ValueT >
	SimRecord& valueChange( const ValueT& oldVal, const ValueT& newVal )
	{
		if( ! details_ )
		{
			initDetailVec();
		}
		details_->push_back( DetailVec::value_type( "old value", oldVal ) );
		details_->push_back( DetailVec::value_type( "new value", newVal ) );
		return *this;
	}

private:
	/// Stores the text message of this record
	StringLiteral text_;
	/// Pointer to the simulation context of the record
	const base::Simulation* sim_;
	/// Pointer to the sender object of the record
	const Producer* sender_;
	/// Simulation time at which the record was created
	base::SimTime time_;
	/// Class scope the record originated from
	TypeInfo scope_;
	/// Stores name-value pairs containing additional record details
	std::tr1::shared_ptr< DetailVec > details_;

private:
	/// Initialize the shared_ptr holding the detail vector
	void initDetailVec();
};

} } // namespace odemx::data

/*
// necessary specialization for DynamicVar to handle long long SimTime as detail value
namespace Poco {
namespace Dynamic {

template <>
class VarHolderImpl<long long>: public VarHolder
{
public:
	VarHolderImpl(long long val): _val(val)
	{
	}

	~VarHolderImpl()
	{
	}

	const std::type_info& type() const
	{
		return typeid(long long);
	}

	void convert(Int8& val) const
	{
		convertToSmaller(_val, val);
	}

	void convert(Int16& val) const
	{
		convertToSmaller(_val, val);
	}

	void convert(Int32& val) const
	{
		convertToSmaller(_val, val);
	}

	void convert(Int64& val) const
	{
		convertToSmaller(_val, val);
	}

	void convert(UInt8& val) const
	{
		convertSignedToUnsigned(_val, val);
	}

	void convert(UInt16& val) const
	{
		convertSignedToUnsigned(_val, val);
	}

	void convert(UInt32& val) const
	{
		convertSignedToUnsigned(_val, val);
	}

	void convert(UInt64& val) const
	{
		convertSignedToUnsigned(_val, val);
	}

	void convert(bool& val) const
	{
		val = (_val != 0);
	}

	void convert(float& val) const
	{
		val = static_cast<float>(_val);
	}

	void convert(double& val) const
	{
		val = static_cast<double>(_val);
	}

	void convert(char& val) const
	{
		UInt8 tmp;
		convert(tmp);
		val = static_cast<char>(tmp);
	}

	void convert(std::string& val) const
	{
		val = odemx::toString( _val );
		//val = NumberFormatter::format(_val);
	}

	void convert(DateTime& dt) const
	{
		dt = Timestamp(_val);
	}

	void convert(LocalDateTime& ldt) const
	{
		ldt = Timestamp(_val);
	}

	void convert(Timestamp& val) const
	{
		val = Timestamp(_val);
	}

	VarHolder* clone() const
	{
		return new VarHolderImpl(_val);
	}

	const long long& value() const
	{
		return _val;
	}

	bool isArray() const
	{
		return false;
	}

	bool isStruct() const
	{
		return false;
	}

	bool isInteger() const
	{
		return std::numeric_limits<long long>::is_integer;
	}

	bool isSigned() const
	{
		return std::numeric_limits<long long>::is_signed;
	}

	bool isNumeric() const
	{
		return std::numeric_limits<long long>::is_specialized;
	}

	bool isString() const
	{
		return false;
	}

private:
	VarHolderImpl();
	VarHolderImpl(const VarHolderImpl&);
	VarHolderImpl& operator = (const VarHolderImpl&);

	long long _val;
};

} } // namespace Poco::Dynamic
*/
#endif /* ODEMX_DATA_SIMRECORD_INCLUDED */
