//
// Copyright (c) 2009-2010 Ronald Kluth
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

/**
 * @file Filter.h
 * @author Ronald Kluth
 * @date created at 2009/06/07
 * @brief Declaration and implementation of class Filter
 * @since 0.1
 */

#ifndef LOG_FILTER_INCLUDED
#define LOG_FILTER_INCLUDED

#include "StringLiteral.h"
#include "detail/InfoTypeFilter.h"
#include "detail/SetInserter.h"
#include "detail/TypeInfo.h"

#include <map>

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

namespace Log {

// forward declaration
class Record;

/**
 * @brief Basic class template for log filters
 * @note If this template is instantiated and @c pass is called, an assertion
 * will fail because for each record type a specialization is required.
 */
template < typename RecordT = Record >
class Filter
{
public:
	bool pass( const RecordT& record ) const
	{
		assert( false && "Missing template specialization for record type" );
		return false;
	}
};

/**
 * @brief A simple filter for log records, based on their text and info type values
 *
 * This class provides a simple filter for log records. It allows for record
 * filtering by comparing each record's info type values to those stored in
 * filter sets. All log channels have a member that may hold a record filter,
 * but filter objects may be owned and used by log consumers as well to filter
 * the records they receive.
 *
 * @b Usage:
 * @code
 * std::tr1::shared_ptr< Log::Filter< Record > > filter( new Log::Filter< Record >() );
 * filter->passNone();
 * filter->add< Log::ScopeInfo >() << "foo()" << "main()";
 * @endcode
 *
 * The above code creates a filter object that allows no records to pass except
 * those with scope "foo()" or scope "main()".
 */
template <>
class Filter< Record >
{
public:
	/// Creation via static method
	static std::tr1::shared_ptr< Filter< Record > > create()
	{
		return std::tr1::shared_ptr< Filter< Record > >( new Filter() );
	}

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

	/// 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;
	}

	/// Add record text values to the filter
	Detail::SetInserter< StringLiteral > addRecordText()
	{
		// remember that the filter has been initialized
		isSet_ = true;
		return Detail::SetInserter< StringLiteral >( recordTexts_ );
	}

	/// Add values of type @c InfoT to the corresponding filter set
	template < typename InfoT >
	Detail::SetInserter< typename InfoT::ValueType > add()
	{
		using namespace std::tr1;
		typedef Detail::InfoTypeFilterImpl< InfoT > FilterType;

		// remember that the filter has been initialized
		isSet_ = true;

		// get the shared pointer to a filter or create a new one with op[]
		InfoTypeFilterPtr& filter = infoTypeFilters_[ typeid( InfoT ) ];

		// if the shared pointer is not initialized, do it now
		if( ! filter )
		{
			filter.reset( new FilterType() );
		}

		// return a set inserter object that can be used with op<<
		// TODO: using ValueType might not work for pointer types
		return Detail::SetInserter< typename InfoT::ValueType >(
				static_pointer_cast< FilterType >( filter )->filterSet_ );
	}

	/// Determine whether the @c record may pass the filter by checking info type sets and evaluating @c passAll_
	bool pass( const Record& record ) const
	{
		// let all records pass if the filter is not set
		if( ! isSet_ )
		{
			return true;
		}

		// check the record text first
		if( recordTexts_.find( record.getText() ) != recordTexts_.end() )
		{
			// match found, the record may only pass if passAll_ is false
			return ! passAll_;
		}

		// check all info type filters for matches in this record
		bool found = false;
		InfoTypeFilterMap::const_iterator iter;
		for( iter = infoTypeFilters_.begin();
			iter != infoTypeFilters_.end(); ++iter )
		{
			found = iter->second->matches( record );
			if( found )
			{
				// match found, the record may only pass if passAll_ is false
				return ! passAll_;
			}
		}
		// no match found, the record may pass if passAll_ is true
		return passAll_;
	}

protected:
	/// Pointer type to manage info type filter objects
	typedef std::tr1::shared_ptr< Detail::InfoTypeFilter > InfoTypeFilterPtr;
	/// Map type that holds smart pointers to info type filters, indexed by type info
	typedef std::map< Detail::TypeInfo, InfoTypeFilterPtr > InfoTypeFilterMap;

	/// Stores whether the filter has been initialized with filter values
	bool isSet_;
	/// Stores whether the filter should by default pass all records or none
	bool passAll_;
	/// Stores text values to be matched when filtering records
	std::set< StringLiteral > recordTexts_;
	/// Stores the actual filter objects for different record info types
	InfoTypeFilterMap infoTypeFilters_;

protected:
	/// Construction protected, use create instead
	Filter()
	:	isSet_( false )
	,	passAll_( true )
	,	recordTexts_()
	,	infoTypeFilters_()
	{
	}
};

} // namespace Log

#endif /* LOG_FILTER_INCLUDED */
