/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright 2018 Danny Robson <danny@nerdcruft.net>
 */

#pragma once

#include <type_traits>


namespace cruft::alloc {
    ///////////////////////////////////////////////////////////////////////////
    /// A trait that evaluates to true if the queried type models cruft::allocator
    template <
        typename AllocatorT,
        typename = std::void_t<>
    >
    struct is_allocator
        : public std::false_type
    { };


    //-------------------------------------------------------------------------
    template <typename AllocatorT>
    struct is_allocator<AllocatorT,
        std::void_t<
            // Provides aligned and unaligned allocation
            decltype(std::declval<AllocatorT> ().allocate (0   )),
            decltype(std::declval<AllocatorT> ().allocate (0, 0)),

            //// Provides aligned an unaligned deallocation
            decltype(std::declval<AllocatorT> ().deallocate (nullptr, 0)),
            decltype(std::declval<AllocatorT> ().deallocate (nullptr, 0, 0)),

            //// Provides capacity/used/remain
            decltype(std::declval<AllocatorT> ().capacity ()),
            decltype(std::declval<AllocatorT> ().used     ()),
            decltype(std::declval<AllocatorT> ().remain   ()),

            decltype(std::declval<AllocatorT> ().begin ()),
            decltype(std::declval<AllocatorT> ().end   ()),

            void
        >
    > : public std::true_type
    { };


    //-------------------------------------------------------------------------
    template <typename AllocatorT>
    constexpr auto is_allocator_v = is_allocator<AllocatorT>::value;


    ///////////////////////////////////////////////////////////////////////////
    template <typename AllocT, typename = std::void_t<>>
    struct has_aligned_allocate : std::false_type {};


    //-------------------------------------------------------------------------
    template <typename AllocT>
    struct has_aligned_allocate<
        AllocT,
        std::void_t<
            decltype (
            std::declval<AllocT> ().allocate(16, 16)
            )
        >
    >: public std::true_type {};



    //-------------------------------------------------------------------------
    template <typename AllocT>
    constexpr auto has_aligned_allocate_v = has_aligned_allocate<AllocT>::value;
}