#pragma once

namespace JabyEngine {
    template <class T, T v>
    struct integral_constant {
        static constexpr T value = v;
  
        typedef T                      value_type;
        typedef integral_constant<T,v> type;

        constexpr operator T() const {
            return v; 
        }
    
        constexpr T operator()() const {
            return v; 
        }
    };

    typedef integral_constant<bool, true>  true_type;
    typedef integral_constant<bool, false> false_type;

    // #############################################

    template<bool B, class T = void>
    struct enable_if {};
 
    template<class T>
    struct enable_if<true, T> {
        typedef T type;
    };

    // #############################################

    template<class T, class U>
    struct is_same {
        static constexpr bool value = false;
    };
 
    template<class T>
    struct is_same<T, T> {
        static constexpr bool value = true;
    };

    // #############################################

    template<bool B, class T, class F>
    struct conditional { 
        using type = T; 
    };
 
    template<class T, class F>
    struct conditional<false, T, F> { 
        using type = F; 
    };

    // #############################################

    template<class...> 
    struct conjunction {
        static constexpr bool value = true;
    };

    template<class B1> 
    struct conjunction<B1> : B1 {
    };

    template<class B1, class... Bn>
    struct conjunction<B1, Bn...> : conditional<bool(B1::value), conjunction<Bn...>, B1>::type {
    };

    // #############################################
    
    template<class T>
    struct is_pointer : false_type {};
 
    template<class T>
    struct is_pointer<T*> : true_type {};
 
    template<class T>
    struct is_pointer<T* const> : true_type {};
 
    template<class T>
    struct is_pointer<T* volatile> : true_type {};
 
    template<class T>
    struct is_pointer<T* const volatile> : true_type {};
    
    // #############################################

    template<typename T, typename... Ts>
    using variadic_force_same = enable_if<conjunction<is_same<T, Ts>...>::value>::type;

    // #############################################
}