// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#ifndef RAPIDJSON_INTERNAL_META_H_
#define RAPIDJSON_INTERNAL_META_H_

#ifndef RAPIDJSON_RAPIDJSON_H_
#error <rapidjson.h> not yet included. Do not include this file directly.
#endif

#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
		RAPIDJSON_DIAG_OFF(effc++)
#endif
#if defined(_MSC_VER)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(6334)
#endif

#if RAPIDJSON_HAS_CXX11_TYPETRAITS
#include <type_traits>
#endif

//@cond RAPIDJSON_INTERNAL
namespace rapidjson {
	namespace internal {

// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching
		template<typename T>
		struct Void {
			typedef void Type;
		};

///////////////////////////////////////////////////////////////////////////////
// BoolType, TrueType, FalseType
//
		template<bool Cond>
		struct BoolType {
			static const bool Value = Cond;
			typedef BoolType Type;
		};
		typedef BoolType<true> TrueType;
		typedef BoolType<false> FalseType;


///////////////////////////////////////////////////////////////////////////////
// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr
//

		template<bool C>
		struct SelectIfImpl {
			template<typename T1, typename T2>
			struct Apply {
				typedef T1 Type;
			};
		};
		template<>
		struct SelectIfImpl<false> {
			template<typename T1, typename T2>
			struct Apply {
				typedef T2 Type;
			};
		};
		template<bool C, typename T1, typename T2>
		struct SelectIfCond : SelectIfImpl<C>::template Apply<T1, T2> {
		};
		template<typename C, typename T1, typename T2>
		struct SelectIf : SelectIfCond<C::Value, T1, T2> {
		};

		template<bool Cond1, bool Cond2>
		struct AndExprCond : FalseType {
		};
		template<>
		struct AndExprCond<true, true> : TrueType {
		};
		template<bool Cond1, bool Cond2>
		struct OrExprCond : TrueType {
		};
		template<>
		struct OrExprCond<false, false> : FalseType {
		};

		template<typename C>
		struct BoolExpr : SelectIf<C, TrueType, FalseType>::Type {
		};
		template<typename C>
		struct NotExpr : SelectIf<C, FalseType, TrueType>::Type {
		};
		template<typename C1, typename C2>
		struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {
		};
		template<typename C1, typename C2>
		struct OrExpr : OrExprCond<C1::Value, C2::Value>::Type {
		};


///////////////////////////////////////////////////////////////////////////////
// AddConst, MaybeAddConst, RemoveConst
		template<typename T>
		struct AddConst {
			typedef const T Type;
		};
		template<bool Constify, typename T>
		struct MaybeAddConst : SelectIfCond<Constify, const T, T> {
		};
		template<typename T>
		struct RemoveConst {
			typedef T Type;
		};
		template<typename T>
		struct RemoveConst<const T> {
			typedef T Type;
		};


///////////////////////////////////////////////////////////////////////////////
// IsSame, IsConst, IsMoreConst, IsPointer
//
		template<typename T, typename U>
		struct IsSame : FalseType {
		};
		template<typename T>
		struct IsSame<T, T> : TrueType {
		};

		template<typename T>
		struct IsConst : FalseType {
		};
		template<typename T>
		struct IsConst<const T> : TrueType {
		};

		template<typename CT, typename T>
		struct IsMoreConst
				: AndExpr<IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>,
						BoolType<IsConst<CT>::Value >= IsConst<T>::Value> >::Type {
		};

		template<typename T>
		struct IsPointer : FalseType {
		};
		template<typename T>
		struct IsPointer<T *> : TrueType {
		};

///////////////////////////////////////////////////////////////////////////////
// IsBaseOf
//
#if RAPIDJSON_HAS_CXX11_TYPETRAITS

		template <typename B, typename D> struct IsBaseOf
			: BoolType< ::std::is_base_of<B,D>::value> {};

#else // simplified version adopted from Boost

		template<typename B, typename D>
		struct IsBaseOfImpl {
			RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0);
			RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0);

			typedef char (&Yes)[1];
			typedef char (&No)[2];

			template<typename T>
			static Yes Check(const D *, T);

			static No Check(const B *, int);

			struct Host {
				operator const B *() const;

				operator const D *();
			};

			enum {
				Value = (sizeof(Check(Host(), 0)) == sizeof(Yes))
			};
		};

		template<typename B, typename D>
		struct IsBaseOf
				: OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D> > >::Type {
		};

#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS


//////////////////////////////////////////////////////////////////////////
// EnableIf / DisableIf
//
		template<bool Condition, typename T = void>
		struct EnableIfCond {
			typedef T Type;
		};
		template<typename T>
		struct EnableIfCond<false, T> { /* empty */ };

		template<bool Condition, typename T = void>
		struct DisableIfCond {
			typedef T Type;
		};
		template<typename T>
		struct DisableIfCond<true, T> { /* empty */ };

		template<typename Condition, typename T = void>
		struct EnableIf : EnableIfCond<Condition::Value, T> {
		};

		template<typename Condition, typename T = void>
		struct DisableIf : DisableIfCond<Condition::Value, T> {
		};

// SFINAE helpers
		struct SfinaeTag {
		};
		template<typename T>
		struct RemoveSfinaeTag;
		template<typename T>
		struct RemoveSfinaeTag<SfinaeTag &(*)(T)> {
			typedef T Type;
		};

#define RAPIDJSON_REMOVEFPTR_(type) \
    typename ::rapidjson::internal::RemoveSfinaeTag \
        < ::rapidjson::internal::SfinaeTag&(*) type>::Type

#define RAPIDJSON_ENABLEIF(cond) \
    typename ::rapidjson::internal::EnableIf \
        <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL

#define RAPIDJSON_DISABLEIF(cond) \
    typename ::rapidjson::internal::DisableIf \
        <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL

#define RAPIDJSON_ENABLEIF_RETURN(cond, returntype) \
    typename ::rapidjson::internal::EnableIf \
        <RAPIDJSON_REMOVEFPTR_(cond), \
         RAPIDJSON_REMOVEFPTR_(returntype)>::Type

#define RAPIDJSON_DISABLEIF_RETURN(cond, returntype) \
    typename ::rapidjson::internal::DisableIf \
        <RAPIDJSON_REMOVEFPTR_(cond), \
         RAPIDJSON_REMOVEFPTR_(returntype)>::Type

	} // namespace internal
} // namespace rapidjson
//@endcond

#if defined(__GNUC__) || defined(_MSC_VER)
RAPIDJSON_DIAG_POP
#endif

#endif // RAPIDJSON_INTERNAL_META_H_
