//
// 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 InfoType.h
 * @author Ronald Kluth
 * @date created at 2009/05/28
 * @brief Declaration and implementation of class template InfoType
 * @since 0.1
 */

#ifndef LOG_DETAIL_INFOTYPE_INCLUDED
#define LOG_DETAIL_INFOTYPE_INCLUDED

#include "EnableIf.h"
#include "Strip.h"
#include "string_literal.hpp"

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

#include <string>

namespace Log {
namespace Detail {

typedef boost::log::string_literal StringLiteral;

/**
 * @class InfoTypeBase
 * @brief Empty base class used to emulate heterogeneous containers
 *
 * This class is used to allow storage of pointers to different @c InfoType<T>
 * objects in containers. The real value is retrieved by using template methods
 * parameterized with the correct info type.
 *
 * @see InfoType, InfoHolder
 */
class InfoTypeBase
{
public:
	virtual ~InfoTypeBase() {}
};

/**
 * @class InfoType
 * @brief Wrapper for arbitrary value types to store additional record information
 * @tparam NameHolderT Unique type to distinguish info types and get a string name
 * @tparam ValueT Type of the wrappper's stored value, must be copyable
 * @tparam EnableT Specialization selector, e.g. @c EnableIf<>
 * @note The default template implementation copies the value.
 *
 * Info type classes can hold arbitrary data of type @c ValueT. The tag type
 * @c TagT is required to distinguish info type objects with the same value
 * type by using their @c typeid. Obviously, the tag types must be unique for
 * this to work properly.
 *
 * @note InfoType and InfoHolder are based on a technique demonstrated
 * by boost::error_info.
 * @note This class template is not intended to be used directly, use the macro
 * @c CPPLOG_DECLARE_INFO_TYPE instead.
 */
template < typename NameHolderT, typename ValueT, typename EnableT = void >
class InfoType
:	public InfoTypeBase
{
public:
	/// Type of the struct providing the info type's string name
	typedef NameHolderT NameHolderType;
	/// Value type definition for template use and value access
	typedef ValueT ValueType;
	/// Actual type of the data, when pointer or reference is stripped
	typedef typename Strip< ValueType >::Type Type;

	/// Construction
	InfoType( Type const& value )
	:	value_( value )
	{
	}

	/// Value retrieval
	Type const* value() const
	{
		return &value_;
	}

	/// Get a string name for this info type
	static StringLiteral const& getName()
	{
		return NameHolderType::getName();
	}

private:
	/// Const value storage, no re-assignment
	Type const value_;
};

#ifndef DOXYGEN_SKIP
/**
 * @brief Specialization of InfoType to hold pointer types
 * @note This specialization only copies the pointer, not the value.
 * @note The fact that the method @c value always returns a pointer to const
 * allows @c InfoHolder::get to always return a pointer, instead of pointer-to-pointer.
 */
#endif
template < typename NameHolderT, typename ValueT >
class InfoType< NameHolderT, ValueT, typename EnableIf< typename std::tr1::is_pointer< ValueT > >::Type >
:	public InfoTypeBase
{
public:
	/// Type of the struct providing the info type's string name
	typedef NameHolderT NameHolderType;
	/// Value type definition for template use and value access
	typedef ValueT ValueType;
	/// Actual type of the data, when pointer or reference is stripped
	typedef typename Strip< ValueType >::Type Type;

	/// Construction
	InfoType( Type const* value )
	:	value_( value )
	{
	}

	/// Value retrieval
	Type const* value() const
	{
		return value_;
	}

	/// Get a string name for this info type
	static StringLiteral const& getName()
	{
		return NameHolderType::getName();
	}

private:
	/// Const value storage, no re-assignment
	Type const* value_;
};

#ifndef DOXYGEN_SKIP
/**
 * @brief Specialization of InfoType to hold reference types
 * @note The specialization only copies the reference, not the value
 * @note The fact that the method @c value always returns a pointer to const
 * allows @c InfoHolder::get to always return a pointer, instead of
 * pointer-to-reference (which is not allowed).
 */
#endif
template < typename NameHolderT, typename ValueT >
class InfoType< NameHolderT, ValueT, typename EnableIf< typename std::tr1::is_reference< ValueT > >::Type >
:	public InfoTypeBase
{
public:
	/// Type of the struct providing the info type's string name
	typedef NameHolderT NameHolderType;
	/// Value type definition for template use and value access
	typedef ValueT ValueType;
	/// Actual type of the data, when pointer or reference is stripped
	typedef typename Strip< ValueType >::Type Type;

	/// Construction
	InfoType( Type const& value )
	:	value_( value )
	{
	}

	/// Value retrieval
	Type const* value() const
	{
		return &value_;
	}

	/// Get a string name for this info type
	static StringLiteral const& getName()
	{
		return NameHolderType::getName();
	}

private:
	/// Const value storage, no re-assignment
	Type const& value_;
};

} } // namespace Log::Detail

#endif /* LOG_DETAIL_INFOTYPE_INCLUDED */
