/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


#ifndef ECXX_CTL_FOUNDATION_H
#define ECXX_CTL_FOUNDATION_H


#include <eCxx/CTL/Types.h>


//
// Implementations of placement operator new and delete
//
inline void* operator new     (size_t, void* p) { return p; }
inline void* operator new   [](size_t, void* p) { return p; }

inline void  operator delete  (void*,  void*  ) {}
inline void  operator delete[](void*,  void*  ) {}


namespace eCxx {


//
// addLValueReference<> and addRValueReference<>
//
namespace internal {

    template <typename ValueT>
    struct typeIdentity { typedef ValueT Type; };

    template <typename ValueT> auto addLValueReference(int) -> typeIdentity<ValueT&>;
    template <typename ValueT> auto addLValueReference(...) -> typeIdentity<ValueT >;

    template <typename ValueT> auto addRValueReference(int) -> typeIdentity<ValueT&&>;
    template <typename ValueT> auto addRValueReference(...) -> typeIdentity<ValueT  >;

} // namespace internal

template <typename ValueT> struct addLValueReference : decltype(internal::addLValueReference<ValueT>(0)) {};

template <typename ValueT> struct addRValueReference : decltype(internal::addRValueReference<ValueT>(0)) {};


//
// removeReference<>
//
template <typename ValueT> struct removeReference           { typedef ValueT Type; };
template <typename ValueT> struct removeReference<ValueT& > { typedef ValueT Type; };
template <typename ValueT> struct removeReference<ValueT&&> { typedef ValueT Type; };


//
// removeExtent<>
//
template <typename ValueT          > struct removeExtent            { typedef ValueT Type; };
template <typename ValueT          > struct removeExtent<ValueT[] > { typedef ValueT Type; };
template <typename ValueT, size_t N> struct removeExtent<ValueT[N]> { typedef ValueT Type; };


//
// removeAllExtents<>
//
template <typename ValueT          > struct removeAllExtents            { typedef                           ValueT        Type; };
template <typename ValueT          > struct removeAllExtents<ValueT[ ]> { typedef typename removeAllExtents<ValueT>::Type Type; };
template <typename ValueT, size_t N> struct removeAllExtents<ValueT[N]> { typedef typename removeAllExtents<ValueT>::Type Type; };


//
// addConst<>, addVolatile<>, and addCV<>
//
template <typename ValueT> struct addConst    { typedef const          ValueT Type; };
template <typename ValueT> struct addVolatile { typedef       volatile ValueT Type; };
template <typename ValueT> struct addCV       { typedef const volatile ValueT Type; };


//
// removeConst<>, removeVolatile<>, and removeCV<>
//
template <typename ValueT> struct removeConst                { typedef ValueT Type; };
template <typename ValueT> struct removeConst<const ValueT>  { typedef ValueT Type; };

template <typename ValueT> struct removeVolatile                  { typedef ValueT Type; };
template <typename ValueT> struct removeVolatile<volatile ValueT> { typedef ValueT Type; };

template <typename ValueT> struct removeCV                        { typedef ValueT Type; };
template <typename ValueT> struct removeCV<const          ValueT> { typedef ValueT Type; };
template <typename ValueT> struct removeCV<      volatile ValueT> { typedef ValueT Type; };
template <typename ValueT> struct removeCV<const volatile ValueT> { typedef ValueT Type; };


//
// removeCVRef<>
//
template <typename ValueT>
struct removeCVRef { typedef typename eCxx::removeCV< typename eCxx::removeReference<ValueT>::Type >::Type Type; };


//
// addPointer<>
//
namespace internal {

    template <typename ValueT> auto addPointer(int) -> typeIdentity<typename eCxx::removeReference<ValueT>::Type*>;
    template <typename ValueT> auto addPointer(...) -> typeIdentity<ValueT>;

} // namespace internal

template <typename ValueT> struct addPointer : decltype(internal::addPointer<ValueT>(0)) {};


//
// removePointer<>
//
template <typename ValueT> struct removePointer                         { typedef ValueT Type;};
template <typename ValueT> struct removePointer<ValueT*               > { typedef ValueT Type;};
template <typename ValueT> struct removePointer<ValueT* const         > { typedef ValueT Type;};
template <typename ValueT> struct removePointer<ValueT*       volatile> { typedef ValueT Type;};
template <typename ValueT> struct removePointer<ValueT* const volatile> { typedef ValueT Type;};


//
// declval<>()
//
template <typename ValueT> typename eCxx::addRValueReference<ValueT>::Type declval() noexcept;


//
// enableIf<> and conditional<>
//
template <bool Condition, typename TrueT = void> struct enableIf              {};
template <                typename TrueT       > struct enableIf<true, TrueT> { typedef TrueT Type; };

template <bool Condition, typename TrueT, typename FalseT> struct conditional                       { typedef TrueT  Type; };
template <                typename TrueT, typename FalseT> struct conditional<false, TrueT, FalseT> { typedef FalseT Type; };


//
// integralConstant<> and boolConstant<>
//
template <typename ValueT, ValueT Val>
struct integralConstant {
    typedef integralConstant Type;
    typedef ValueT           ValueType;

    static constexpr ValueType Value = Val;

    constexpr operator ValueType()   const noexcept { return Value; }
    constexpr ValueType operator()() const noexcept { return Value; }
};

template <bool Val>
using boolConstant = eCxx::integralConstant<bool, Val>;

using trueType  = eCxx::boolConstant<true >;
using falseType = eCxx::boolConstant<false>;


//
// conjunction<>
//
template <typename...>
struct conjunction : eCxx::trueType {};

template <typename ValueT>
struct conjunction<ValueT> : ValueT {};

template <typename ValueT, typename... ValueTs>
struct conjunction<ValueT, ValueTs...> : eCxx::conditional<
    static_cast<bool>(ValueT::Value), eCxx::conjunction<ValueTs...>, ValueT
>::Type {};


//
// disjunction<>
//
template <typename...>
struct disjunction : eCxx::falseType {};

template <typename ValueT>
struct disjunction<ValueT> : ValueT {};

template <typename ValueT, typename... ValueTs>
struct disjunction<ValueT, ValueTs...> : eCxx::conditional<
    static_cast<bool>(ValueT::Value), ValueT, disjunction<ValueTs...>
>::Type {};


//
// negation<>
//
template <typename ValueT>
struct negation : eCxx::boolConstant< !static_cast<bool>(ValueT::Value) > {};


//
// isSame<>
//
template <typename ValueT, typename ValueU> struct isSame                 : eCxx::falseType {};
template <typename ValueT                 > struct isSame<ValueT, ValueT> : eCxx::trueType  {};


//
// isAllSame<>
//
template <typename ValueRefT, typename... ValueTs>
using isAllSame = eCxx::enableIf< eCxx::conjunction< eCxx::isSame<ValueRefT, ValueTs>... >::Value >;


//
// isConst<>
//
template <typename ValueT> struct isConst               : eCxx::falseType {};
template <typename ValueT> struct isConst<const ValueT> : eCxx::trueType  {};


//
// isVolatile<>
//
template <typename ValueT> struct isVolatile                  : eCxx::falseType {};
template <typename ValueT> struct isVolatile<volatile ValueT> : eCxx::trueType {};


//
// isArray<>
//
template <typename ValueT          > struct isArray            : eCxx::falseType {};
template <typename ValueT          > struct isArray<ValueT[] > : eCxx::trueType  {};
template <typename ValueT, size_t N> struct isArray<ValueT[N]> : eCxx::trueType  {};


//
// isReference<>, isLValueReference<>, and isRValueReference<>
//
template <typename ValueT> struct isReference           : eCxx::falseType {};
template <typename ValueT> struct isReference<ValueT&>  : eCxx::trueType  {};
template <typename ValueT> struct isReference<ValueT&&> : eCxx::trueType  {};

template <typename ValueT> struct isLValueReference          : eCxx::falseType {};
template <typename ValueT> struct isLValueReference<ValueT&> : eCxx::trueType  {};

template <typename ValueT> struct isRValueReference           : eCxx::falseType {};
template <typename ValueT> struct isRValueReference<ValueT&&> : eCxx::trueType  {};


//
// isNullPointer<>
//
typedef decltype(nullptr) nullptr_t;

template <typename ValueT>
struct isNullPointer : eCxx::isSame<eCxx::nullptr_t, typename eCxx::removeCV<ValueT>::Type> {};


//
// isPointer<>
//
namespace internal {

    template <typename ValueT> struct isPointer          : eCxx::falseType {};
    template <typename ValueT> struct isPointer<ValueT*> : eCxx::trueType  {};

} // namespace internal

template <typename ValueT>
struct isPointer : internal::isPointer<typename eCxx::removeCV<ValueT>::Type> {};


//
// isMemberPointer<>
//
namespace internal {

    template <typename ValueT                 > struct isMemberPointer                   : eCxx::falseType {};
    template <typename ValueT, typename ValueU> struct isMemberPointer<ValueT ValueU::*> : eCxx::trueType  {};

} // namespace internal

template <typename ValueT> struct isMemberPointer : internal::isMemberPointer<typename eCxx::removeCV<ValueT>::Type> {};


//
// isFunction<>
//
template <typename ValueT>
struct isFunction : eCxx::integralConstant< bool,
                                            !eCxx::isConst<const ValueT>::Value && !eCxx::isReference<ValueT>::Value
                                          > {};


//
// isMemberFunctionPointer<>
//
namespace internal {

    template <typename ValueT>
    struct isMemberFunctionPointer : eCxx::falseType {};

    template <typename ValueT, typename ValueU>
    struct isMemberFunctionPointer<ValueT ValueU::*> : eCxx::isFunction<ValueT> {};

} // namespace internal


template <typename ValueT>
struct isMemberFunctionPointer : internal::isMemberFunctionPointer<typename eCxx::removeCV<ValueT>::Type> {};


//
// isVoid<>
//
template <typename ValueT>
struct isVoid : eCxx::isSame<void, typename eCxx::removeCV<ValueT>::Type> {};


//
// isEmpty<>
//
template <typename ValueT>
struct isEmpty : eCxx::integralConstant<bool, __is_empty(ValueT)> {};


//
// isTrivial<>
//
template <typename ValueT>
struct isTrivial : eCxx::integralConstant<bool, __is_trivial(ValueT)> {};


//
// isTriviallyCopyable<>
//
#if __GNUG__ && __GNUC__ < 5

template <typename ValueT>
struct isTriviallyCopyable : eCxx::integralConstant<bool, __has_trivial_copy(ValueT)> {};

#else

template <typename ValueT>
struct isTriviallyCopyable : eCxx::integralConstant<bool, __is_trivially_copyable(ValueT)> {};

#endif


//
// isStandardLayout<>
//
template <typename ValueT>
struct isStandardLayout : eCxx::integralConstant<bool, __is_standard_layout(ValueT)> {};


//
// isPOD<>
//
template <typename ValueT>
struct isPOD : eCxx::integralConstant< bool,
                                       eCxx::isTrivial<ValueT>::Value && eCxx::isStandardLayout<ValueT>::Value
                                     > {};


//
// isEnum<>
//
template <typename ValueT>
struct isEnum : eCxx::integralConstant<bool, __is_enum(ValueT)> {};


//
// isUnion<>
//
template <typename ValueT>
struct isUnion : eCxx::integralConstant<bool, __is_union(ValueT)> {};


//
// isClass<>
//
namespace internal {

    template <typename ValueT>
    eCxx::integralConstant<bool, !eCxx::isUnion<ValueT>::Value> isClass(int ValueT::*);

    template <typename>
    eCxx::falseType isClass(...);

} // namespace internal

template <typename ValueT>
struct isClass : decltype( internal::isClass<ValueT>(nullptr) ) {};


//
// isBaseOf<>
//
namespace internal {

    template <typename ValueB>
    eCxx::trueType testPtrConvertible(const volatile ValueB*);

    template <typename>
    eCxx::falseType testPtrConvertible(const volatile void*);

    template <typename, typename>
    auto isBaseOf(...) -> eCxx::trueType;

    template <typename ValueB, typename ValueD>
    auto isBaseOf(int) -> decltype( testPtrConvertible<ValueB>( static_cast<ValueD*>(nullptr) ) );

} // namespace internal

template <typename ValueB, typename ValueD>
struct isBaseOf : eCxx::integralConstant<
    bool,
    eCxx::isClass<ValueB>::Value && eCxx::isClass<ValueD>::Value && decltype( internal::isBaseOf<ValueB, ValueD>(0) )::Value
> {};


//
// isIntegral<>, isFloatingPoint<>, and isArithmetic<>
//
namespace internal {

    template <typename> struct isIntegral                      : eCxx::falseType {};
    template <>         struct isIntegral<              bool > : eCxx::trueType  {};
    template <>         struct isIntegral<              char > : eCxx::trueType  {};
    template <>         struct isIntegral<  signed      char > : eCxx::trueType  {};
    template <>         struct isIntegral<unsigned      char > : eCxx::trueType  {};
    template <>         struct isIntegral<  signed      short> : eCxx::trueType  {};
    template <>         struct isIntegral<unsigned      short> : eCxx::trueType  {};
    template <>         struct isIntegral<  signed      int  > : eCxx::trueType  {};
    template <>         struct isIntegral<unsigned      int  > : eCxx::trueType  {};
    template <>         struct isIntegral<  signed      long > : eCxx::trueType  {};
    template <>         struct isIntegral<unsigned      long > : eCxx::trueType  {};
    template <>         struct isIntegral<  signed long long > : eCxx::trueType  {};
    template <>         struct isIntegral<unsigned long long > : eCxx::trueType  {};

    template <typename> struct isFloatingPoint              : eCxx::falseType {};
    template <>         struct isFloatingPoint<      float> : eCxx::trueType  {};
    template <>         struct isFloatingPoint<     double> : eCxx::trueType  {};
    template <>         struct isFloatingPoint<long double> : eCxx::trueType  {};

} // namespace internal

template <typename ValueT>
struct isIntegral : internal::isIntegral<typename eCxx::removeCV<ValueT>::Type> {};

template <typename ValueT>
struct isFloatingPoint : internal::isFloatingPoint<typename eCxx::removeCV<ValueT>::Type> {};

template <typename ValueT>
struct isArithmetic : eCxx::integralConstant< bool,
                                              eCxx::isIntegral<ValueT>::Value || eCxx::isFloatingPoint<ValueT>::Value
                                            > {};


//
// isFundamental<>
//
template <typename ValueT>
struct isFundamental : eCxx::integralConstant< bool,
                                               eCxx::isNullPointer<ValueT>::Value || eCxx::isVoid<ValueT>::Value || eCxx::isArithmetic<ValueT>::Value
                                             > {};


//
// isCompound<>
//
template <typename ValueT>
struct isCompound : eCxx::integralConstant<bool, !eCxx::isFundamental<ValueT>::Value> {};


//
// isScalar<>
//
template <typename ValueT>
struct isScalar : eCxx::integralConstant<
    bool,
    eCxx::isNullPointer  <ValueT>::Value ||
    eCxx::isArithmetic   <ValueT>::Value ||
    eCxx::isPointer      <ValueT>::Value ||
    eCxx::isMemberPointer<ValueT>::Value ||
    eCxx::isEnum         <ValueT>::Value
> {};


//
// isObject<>
//
template <typename ValueT>
struct isObject : eCxx::integralConstant<
    bool,
    eCxx::isArray <ValueT>::Value ||
    eCxx::isUnion <ValueT>::Value ||
    eCxx::isClass <ValueT>::Value ||
    eCxx::isScalar<ValueT>::Value
> {};


//
// addressOf<>()
//
template <typename ValueT>
inline typename eCxx::enableIf<eCxx::isObject<ValueT>::Value, ValueT*>::Type addressOf(ValueT& arg) noexcept
{ return reinterpret_cast<ValueT*>( &const_cast<char&>( reinterpret_cast<const volatile char&>(arg) ) ); }

template <typename ValueT>
inline typename eCxx::enableIf<!eCxx::isObject<ValueT>::Value, ValueT*>::Type addressOf(ValueT& arg) noexcept
{ return &arg; }


//
// isSigned<>
//
namespace internal {

    template <typename ValueT, bool = eCxx::isArithmetic<ValueT>::Value>
    struct isSigned : eCxx::integralConstant<bool, ValueT(-1) < ValueT(0)> {};

    template <typename ValueT>
    struct isSigned<ValueT, false> : eCxx::falseType {};

} // namespace internal

template <typename ValueT>
struct isSigned : internal::isSigned<ValueT>::Type {};


//
// isUnsigned<>
//
namespace internal {

    template <typename ValueT, bool = eCxx::isArithmetic<ValueT>::Value>
    struct isUnsigned : eCxx::integralConstant<bool, ValueT(0) < ValueT(-1)> {};

    template <typename ValueT>
    struct isUnsigned<ValueT, false> : eCxx::falseType {};

} // namespace internal

template <typename ValueT>
struct isUnsigned : internal::isUnsigned<ValueT>::Type {};


//
// makeSigned<> and makeUnsigned<>
//
template <typename ValueT> struct makeSigned                     { typedef ValueT           Type; };
template <>                struct makeSigned<         char     > { typedef signed char      Type; };
template <>                struct makeSigned<  signed char     > { typedef signed char      Type; };
template <>                struct makeSigned<unsigned char     > { typedef signed char      Type; };
template <>                struct makeSigned<  signed short    > { typedef signed short     Type; };
template <>                struct makeSigned<unsigned short    > { typedef signed short     Type; };
template <>                struct makeSigned<  signed int      > { typedef signed int       Type; };
template <>                struct makeSigned<unsigned int      > { typedef signed int       Type; };
template <>                struct makeSigned<  signed long     > { typedef signed long      Type; };
template <>                struct makeSigned<unsigned long     > { typedef signed long      Type; };
template <>                struct makeSigned<  signed long long> { typedef signed long long Type; };
template <>                struct makeSigned<unsigned long long> { typedef signed long long Type; };

template <typename ValueT> struct makeUnsigned                     { typedef ValueT             Type; };
template <>                struct makeUnsigned<         char     > { typedef unsigned char      Type; };
template <>                struct makeUnsigned<  signed char     > { typedef unsigned char      Type; };
template <>                struct makeUnsigned<unsigned char     > { typedef unsigned char      Type; };
template <>                struct makeUnsigned<  signed short    > { typedef unsigned short     Type; };
template <>                struct makeUnsigned<unsigned short    > { typedef unsigned short     Type; };
template <>                struct makeUnsigned<  signed int      > { typedef unsigned int       Type; };
template <>                struct makeUnsigned<unsigned int      > { typedef unsigned int       Type; };
template <>                struct makeUnsigned<  signed long     > { typedef unsigned long      Type; };
template <>                struct makeUnsigned<unsigned long     > { typedef unsigned long      Type; };
template <>                struct makeUnsigned<  signed long long> { typedef unsigned long long Type; };
template <>                struct makeUnsigned<unsigned long long> { typedef unsigned long long Type; };


//
// extent<>
//
template <typename ValueT, size_t N = 0      > struct extent               : eCxx::integralConstant<size_t, 0> {};
template <typename ValueT                    > struct extent<ValueT[ ], 0> : eCxx::integralConstant<size_t, 0> {};
template <typename ValueT, size_t I          > struct extent<ValueT[I], 0> : eCxx::integralConstant<size_t, I> {};

template <typename ValueT,           size_t N> struct extent<ValueT[ ], N> : eCxx::extent<ValueT, N - 1> {};
template <typename ValueT, size_t I, size_t N> struct extent<ValueT[I], N> : eCxx::extent<ValueT, N - 1> {};


//
// isConvertible<>
//
namespace internal {

    template <typename> using trueTypeFor = eCxx::trueType;

    template <typename T> auto testReturnable(int) -> trueTypeFor<T()>;
    template <typename >  auto testReturnable(...) -> eCxx::falseType;

    template <typename FromT, typename ToT>
    auto testNonVoidConvertible(int) -> trueTypeFor<
        decltype( eCxx::declval<void(&)(ToT)>()( eCxx::declval<FromT>() ) )
    >;

    template <typename, typename>
    auto testNonVoidConvertible(...) -> eCxx::falseType;

} // namespace internal

template <typename From, typename To>
struct isConvertible : eCxx::integralConstant<
    bool,
    (
        decltype( internal::testReturnable        <      To>(0) )::Value &&
        decltype( internal::testNonVoidConvertible<From, To>(0) )::Value
    )
    ||
    (
        eCxx::isVoid<From>::Value && eCxx::isVoid<To>::Value
    )
>
{};


//
// isAssignable<>, isCopyAssignable<>, and isMoveAssignable<>
//
namespace internal {

    template <typename ValueT, typename ValueU>
    struct isAssignable {
        template <typename ValueT1, typename ValueU1, typename = decltype( eCxx::declval<ValueT1>() = eCxx::declval<ValueU1>() ) >
        static eCxx::trueType testAssignable(int);

        template <typename, typename>
        static eCxx::falseType testAssignable(...);

        typedef decltype( testAssignable<ValueT, ValueU>(0) ) Type;
    };
}

template <typename ValueT, typename ValueU>
struct isAssignable : internal::isAssignable<ValueT, ValueU>::Type {};

template <typename ValueT>
struct isCopyAssignable : eCxx::isAssignable< typename eCxx::addLValueReference<ValueT>::Type, typename eCxx::addLValueReference<const ValueT>::Type> {};

template <typename ValueT>
struct isMoveAssignable : eCxx::isAssignable< typename eCxx::addLValueReference<ValueT>::Type, typename eCxx::addRValueReference<ValueT>::Type> {};


//
// decay<>
//
namespace internal {

    template <typename ValueT>
    struct tag { typedef ValueT Type; };

    template <typename ValueT>
    struct decay2 : eCxx::removeCV<ValueT> {};

    template <typename ValueR, typename... Args>
    struct decay2<ValueR(Args...)> : tag<ValueR(*)(Args...)> {};

    template <typename ValueT>
    struct decay1 : decay2<ValueT> {};

    template <typename ValueT, size_t N>
    struct decay1<ValueT[N]> : tag<ValueT*> {};

} // namespace internal

template <typename ValueT>
struct decay : internal::decay1<typename eCxx::removeReference<ValueT>::Type> {};


} // namespace eCxx


#endif
