//
// 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 InfoTypeLoop.h
 * @author Ronald Kluth
 * @date created at 2009/06/09
 * @brief Declaration and implementation of class template InfoTypeLoop,
 * SizeOf and TypeOf for tuples
 * @since 0.1
 */

#ifndef LOG_DETAIL_INFOTYPELOOP_INCLUDED
#define LOG_DETAIL_INFOTYPELOOP_INCLUDED

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


namespace Log {
namespace Detail {

/**
 * @brief Class type to represent empty type lists
 *
 * This empty class is used as default template parameter for default
 * output components. It is needed in case the logged records do not contain
 * any info types to extract. Extraction of a NullList stops the InfoTypeLoop
 * and therefore does nothing.
 */
struct NullList {};

/**
 * @class SizeOf
 * @brief Determine the number types of an info type list
 * @tparam InfoListT A list of info types (must be an @c std::tr1::tuple)
 *
 * This class provides a wrapper over @c std::tr1::tuple_size to determine
 * the number of types in @c InfoListT at compile time.
 */
template < typename InfoListT >
struct SizeOf
{
	enum { value = std::tr1::tuple_size< InfoListT >::value };
};

#ifndef DOXYGEN_SKIP
/// Template specialization for @c NullList
#endif
template<>
struct SizeOf< NullList >
{
	enum { value = 0 };
};

/**
 * @class TypeOf
 * @brief Determine the type of an element in a list of info types
 * @tparam InfoListT A list of info types
 * @tparam Index The index of the element whose type is to be determined
 *
 * This class provides a wrapper over @c std::tr1::tuple_element to determine
 * the type of the @c InfoListT type at position @c Index
 */
template < typename InfoListT, int Index >
struct TypeOf
{
	typedef typename std::tr1::tuple_element< Index, InfoListT >::type type;
};

#ifndef DOXYGEN_SKIP
/// Template specialization for @c NullList
#endif
template < int Index >
struct TypeOf< NullList, Index >
{
};

/**
 * @class InfoTypeLoop
 * @brief Loop type used to iterate over an info type list
 * @tparam FuncT A functor type to be called for each info type
 * @tparam InfoHolderT A type that allows transporting arbitrary info type data
 * @tparam InfoListT A list of info types
 * @tparam Index The index of the current recursion in the list
 *
 * @note The template parameter FuncT must support the follwing signature:
 * @code
 * template < class InfoT > func( const std::string&, const InfoT& );
 * @endcode
 * with @c InfoT being the same as @c InfoHolderT::ValueType.
 *
 * @see Log::Record::iterateInfo
 */
template <
	typename FuncT,
	typename InfoHolderT,
	typename InfoListT,
	int Index
>
struct InfoTypeLoop
{
	static void call( FuncT& func, const InfoHolderT& holder )
	{
		// recurse first
		InfoTypeLoop< FuncT, InfoHolderT, InfoListT, Index - 1 >::call( func, holder );

		typedef typename TypeOf< InfoListT, Index >::type CurrentInfo;
		typename CurrentInfo::Type const* valuePtr = holder.template get< CurrentInfo >();

		// call func upon return to keep the correct order
		if( valuePtr )
		{
			func( CurrentInfo::getName(), *valuePtr, true );
		}
		else
		{
			func( CurrentInfo::getName(), 0, false );
		}
	}
};

#ifndef DOXYGEN_SKIP
/**
 * @brief Template specialization for index 0, terminates the recursion
 */
#endif
template <
	typename FuncT,
	typename InfoHolderT,
	typename InfoListT
>
struct InfoTypeLoop< FuncT, InfoHolderT, InfoListT, 0 >
{
	static void call( FuncT& func, const InfoHolderT& holder )
	{
		typedef typename TypeOf< InfoListT, 0 >::type CurrentInfo;
		typename CurrentInfo::Type const* valuePtr = holder.template get< CurrentInfo >();

		// stop recursion, call func( name, value );
		if( valuePtr )
		{
			func( CurrentInfo::getName(), *valuePtr, true );
		}
		else
		{
			func( CurrentInfo::getName(), 0, false );
		}
	}
};

#ifndef DOXYGEN_SKIP
/**
 * @brief Template specialization for empty info type list
 */
#endif
template <
	typename FuncT,
	typename InfoHolderT,
	int Index
>
struct InfoTypeLoop< FuncT, InfoHolderT, NullList, Index >
{
	static void call( FuncT& func, const InfoHolderT& holder )
	{
	}
};

} } // namespace Log::Detail

#endif /* LOG_DETAIL_INFOTYPELOOP_INCLUDED */
