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

#ifndef ODEMX_DATA_SIMRECORDFILTER_INCLUDED
#define ODEMX_DATA_SIMRECORDFILTER_INCLUDED

#include <odemx/data/Producer.h>
#include <CppLog/Filter.h>

namespace Log {

/**
 * @brief Specialization of class template Filter for simulation records
 *
 * This template specialization implements the functionality used by the
 * class SimRecordFilter. The filtering is based on the concept of filter sets,
 * meaning that users can add values in several categories, which are checked
 * for matching values in SimRecord objects. For example, one might want to
 * filter by producer type and hence adds the typeid of that specific class.
 * All record originating from objects of that class will then match that
 * entry in the filter set.
 *
 * However, matching records are not necessarily filtered out because the
 * filter supports two different modes: pass all / pass none. In the first
 * case, all records may pass, except those with a match, while in the second
 * case all records are blocked and only those that match filter sets may pass.
 */
template <>
class Filter< odemx::data::SimRecord >
{
public:
	/// Construction
	Filter()
	:	isSet_( false )
	,	passAll_( true )
	{}

	/// Destruction
	virtual ~Filter() {}

	/// Only filter out the log records matched by the filter
	void passAll()
	{
		isSet_ = true;
		passAll_ = true;
	}

	/// Filter out all log records except those matched by the filter
	void passNone()
	{
		isSet_ = true;
		passAll_ = false;
	}

	/**
	 * @brief Add record text values to the filter
	 *
	 * @note The method returns a set inserter object that offers a
	 * streaming operator overload (<<) so that several values can be
	 * added in one call.
	 *
	 * Usage:
	 * @code
	 *   filter->addRecordText() << "create" << "destroy";
	 * @endcode
	 */
	Detail::SetInserter< StringLiteral > addRecordText()
	{
		// remember that the filter has been initialized
		isSet_ = true;
		return Detail::SetInserter< StringLiteral >( recordTexts_ );
	}

	/**
	 * @brief Add producer labels to filter out log data from specific sender objects
	 * @note The method also returns a set inserter object for streaming values.
	 * @see addRecordText
	 */
	Detail::SetInserter< odemx::data::Label > addProducerLabel()
	{
		// remember that the filter has been initialized
		isSet_ = true;
		return Detail::SetInserter< odemx::data::Label >( senderLabels_ );
	}

	/**
	 * @brief Add producer type IDs to filter out log records from specific classes
	 * @note The method returns a set inserter object for streaming type IDs.
	 *
	 * Usage:
	 * @code
	 *   filter->addProducerType() << typeid(SomeProcess) << typeid(SomeEvent);
	 * @endcode
	 */
	Detail::SetInserter< odemx::data::TypeInfo > addProducerType()
	{
		// remember that the filter has been initialized
		isSet_ = true;
		return Detail::SetInserter< odemx::data::TypeInfo >( senderTypes_ );
	}

	/**
	 * @brief Add type IDs to filter out log records from specific class scopes
	 * @note The method also returns a set inserter object for streaming type IDs.
	 * @see addProducerType
	 *
	 * This is particularly useful for filtering in class hierarchies.
	 * For example, on might not want to see records produced by a base
	 * class. This method makes it possible to filter these out as long as
	 * base class provides scope infomation with its records. All ODEMx
	 * components do.
	 */
	Detail::SetInserter< odemx::data::TypeInfo > addRecordScope()
	{
		// remember that the filter has been initialized
		isSet_ = true;
		return Detail::SetInserter< odemx::data::TypeInfo >( recordScopes_ );
	}

	/// Reset filter to default values, which effectively disables it
	void resetFilter()
	{
		senderLabels_.clear();
		senderTypes_.clear();
		recordScopes_.clear();
		recordTexts_.clear();
		isSet_ = false;
		passAll_ = true;
	}

	/**
	 * @brief This method implements the filter interface
	 * @param record The currently processed log record
	 * @return true if the record may pass the filter, false otherwise
	 *
	 * If the filter is not set, all records may pass. Otherwise, all four
	 * filter sets are checked for matching values. Should the record contain
	 * at least one value that is also stored in a filter set, it further
	 * depends on the filter mode whether the record may pass, in which case
	 * the method returns @c true.
	 */
	bool pass( const odemx::data::SimRecord& record ) const
	{
		if( ! isSet_ )
		{
			return true;
		}

		const odemx::data::Producer& sender = record.getSender();

		// check the record text, sender label, sender type and record scope
		if( recordTexts_.find( record.getText() ) != recordTexts_.end()
			|| senderLabels_.find( sender.getLabel() ) != senderLabels_.end()
			|| senderTypes_.find( sender.getType() ) != senderTypes_.end()
			|| recordScopes_.find( record.getScope() ) != recordScopes_.end() )
		{
			// match found, the record may only pass if passAll_ is false
			return ! passAll_;
		}

		// no match found, passing depends on pass mode only
		return passAll_;
	}

protected:
	/// Stores whether the filter is active, i.e. contains filter values or uses pass none
	bool isSet_;
	/// Stores the filter mode, the default is pass all
	bool passAll_;
	/// Stores text values to be matched when filtering records
	std::set< StringLiteral > recordTexts_;
	/// Stores sender labels to match for filtering
	std::set< odemx::data::Label > senderLabels_;
	/// Stores type IDs to filter by record sender type
	std::set< odemx::data::TypeInfo > senderTypes_;
	/// Stores type IDs for filtering records by the class scope they originated from
	std::set< odemx::data::TypeInfo > recordScopes_;
};

} // namespace Log

namespace odemx {
namespace data {

/**
 * @brief Specialized filter class that can handle SimRecord objects
 *
 * The functionality of this class is provided by the template specialization
 * Log::Filter<SimRecord>. The filtering is based on the concept of filter sets,
 * meaning that users can add values in several categories, which are checked
 * for matching values in SimRecord objects. For example, one might want to
 * filter by producer type and hence adds the typeid of that specific class.
 * All record originating from objects of that class will then match that
 * entry in the filter set.
 *
 * However, matching records are not necessarily filtered out because the
 * filter supports two different modes: pass all / pass none. In the first
 * case, all records may pass, except those with a match, while in the second
 * case all records are blocked and only those that match filter sets may pass.
 *
 * Usage:
 * @code
 *   filter->addRecordText() << "create" << "destroy";
 *   filter->addProducerType() << typeid(SomeClass);
 *   filter->addProducerLabel() << "DefaultSimulation";
 *   filter->addRecordScope() << typeid(Process) << typeid(Event);
 * @endcode
 *
 * @see Log::Filter<SimRecord>
 */
class SimRecordFilter
:	public Log::Filter< SimRecord >
{
public:
	/// Creation via static method to enforce shared_ptr-usage
	static std::tr1::shared_ptr< SimRecordFilter > create();
	/// Destruction
	virtual ~SimRecordFilter();

protected:
	/// Construction private, use create instead
	SimRecordFilter();
};

/// Pointer type to manage SimRecordFilter objects
typedef std::tr1::shared_ptr< SimRecordFilter > SimRecordFilterPtr;

} } // namespace odemx::data

#endif /* ODEMX_DATA_SIMRECORDFILTER_INCLUDED */
