////////////////////////////////////////////////////////////////////////////////
// The Loki Library
// Copyright (c) 2001 by Andrei Alexandrescu
// This code accompanies the book:
// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design
//     Patterns Applied". Copyright (c) 2001. Addison-Wesley.
// Permission to use, copy, modify, distribute and sell this software for any
//     purpose is hereby granted without fee, provided that the above copyright
//     notice appear in all copies and that both that copyright notice and this
//     permission notice appear in supporting documentation.
// The author or Addison-Wesley Longman make no representations about the
//     suitability of this software for any purpose. It is provided "as is"
//     without express or implied warranty.
////////////////////////////////////////////////////////////////////////////////

/**
 * @file detail/TypeInfo.h
 * @brief Adapted Loki::TypeInfo from Andrei Alexandrescu's Modern C++ Design
 * @since 0.1
 */

#ifndef LOG_DETAIL_TYPEINFO_INCLUDED
#define LOG_DETAIL_TYPEINFO_INCLUDED

#include "TypeToString.h"

#include <cassert>
#include <string>
#include <typeinfo>
#ifdef _MSC_VER
#include <functional>
#else
#include <tr1/functional>
#endif

namespace Log {
namespace Detail {

/**
 * @brief This class offers a first-class, comparable wrapper over std::type_info
 *
 * Sometimes it is useful to be able to store type infomation objects in standard
 * containers, which is not possible with @c std::type_info. The logging library
 * uses it as key type in an std::map to look up info type values transported in
 * record objects.
 *
 * @note This is a modified version of Andrei Alexandrescu's Loki::TypeInfo.
 */
class TypeInfo
{
public:
	/// Required for default construction and initialization check
	class Null {};

	/// Default-Construction (needed for containers)
	TypeInfo();
	/// Construction (also conversion operator)
	TypeInfo( const std::type_info& );

	/// Access for the wrapped std::type_info
	const std::type_info& get() const;
	/// Check if the type info is not TypeInfo::Null
	bool isSet() const;
	/// Get a (demangled) string representation of the type name
	const std::string toString() const;

	/// Compatibility function
	bool before( const TypeInfo& rhs ) const;
	/// Compatibility function
	const char* name() const;

private:
	/// Pointer to the actual std::type_info object
	const std::type_info* pInfo_;
};

//------------------------------------------------------construction/destruction

inline TypeInfo::TypeInfo()
{
	pInfo_ = &typeid( TypeInfo::Null );
	assert( pInfo_ );
}

inline TypeInfo::TypeInfo( const std::type_info& type )
:	pInfo_( &type )
{
	assert( pInfo_ );
}

//----------------------------------------------------------comparison operators

inline bool operator==( const TypeInfo& lhs, const TypeInfo& rhs )
{
	return ( lhs.get() == rhs.get() ) != 0;
}

inline bool operator<( const TypeInfo& lhs, const TypeInfo& rhs )
{
	return lhs.before( rhs );
}

inline bool operator!=( const TypeInfo& lhs, const TypeInfo& rhs )
{
	return ! ( lhs == rhs );
}

inline bool operator>( const TypeInfo& lhs, const TypeInfo& rhs )
{
	return rhs < lhs;
}

inline bool operator<=( const TypeInfo& lhs, const TypeInfo& rhs )
{
	return ! ( lhs > rhs );
}

inline bool operator>=( const TypeInfo& lhs, const TypeInfo& rhs )
{
	return ! ( lhs < rhs );
}

//----------------------------------------------------------------access methods

inline bool TypeInfo::isSet() const
{
	return ( *this != typeid(TypeInfo::Null) );
}

inline const std::type_info& TypeInfo::get() const
{
	assert( pInfo_ );
	return *pInfo_;
}

inline const std::string TypeInfo::toString() const
{
	assert( pInfo_ );
	return typeToString( *pInfo_ );
}

//---------------------------------------------------------compatibility methods

inline bool TypeInfo::before( const TypeInfo& rhs ) const
{
	assert( pInfo_ );
	// type_info::before return type is int in some VC libraries
	return pInfo_->before( *rhs.pInfo_ ) != 0;
}

inline const char* TypeInfo::name() const
{
	assert( pInfo_ );
	return pInfo_->name();
}

} } // namespace Log::Detail

namespace std {
namespace tr1 {

template <>
struct hash< Log::Detail::TypeInfo >
{
	std::size_t operator ()( const Log::Detail::TypeInfo& val ) const
    { // return hash value for val
		hash< const std::type_info* > make_hash;
		return make_hash( &val.get() );
    }
};

} } // namespace std::tr1

#endif /* LOG_DETAIL_TYPEINFO_INCLUDED */
