/*
 * Copyright (c) 2012-2014 Glen Joseph Fernandes
 * glenfe at live dot com
 *
 * Distributed under the Boost Software License,
 * Version 1.0. (See accompanying file LICENSE_1_0.txt
 * or copy at http://boost.org/LICENSE_1_0.txt)
 */
#ifndef BOOST_SMART_PTR_DETAIL_ARRAY_UTILITY_HPP
#define BOOST_SMART_PTR_DETAIL_ARRAY_UTILITY_HPP

#include <boost/config.hpp>
#include <boost/type_traits/has_trivial_constructor.hpp>
#include <boost/type_traits/has_trivial_destructor.hpp>
#if !defined(BOOST_NO_CXX11_ALLOCATOR)
#include <memory>
#endif

namespace boost {
    namespace detail {
        typedef boost::true_type  ms_is_trivial;
        typedef boost::false_type ms_no_trivial;

        template<class T>
        inline void ms_destroy(T*, std::size_t, ms_is_trivial) {
        }

        template<class T>
        inline void ms_destroy(T* memory, std::size_t size, ms_no_trivial) {
            for (std::size_t i = size; i > 0;) {
                memory[--i].~T();
            }
        }

        template<class T>
        inline void ms_destroy(T* memory, std::size_t size) {
            boost::has_trivial_destructor<T> trivial;
            ms_destroy(memory, size, trivial);
        }

        template<class T>
        inline void ms_init(T* memory, std::size_t size, ms_is_trivial) {
            for (std::size_t i = 0; i < size; i++) {
                void* p1 = memory + i;
                ::new(p1) T();
            }
        }

        template<class T>
        inline void ms_init(T* memory, std::size_t size, ms_no_trivial) {
#if !defined(BOOST_NO_EXCEPTIONS)
            std::size_t i = 0;
            try {
                for (; i < size; i++) {
                    void* p1 = memory + i;
                    ::new(p1) T();
                }
            } catch (...) {
                ms_destroy(memory, i);
                throw;
            }
#else
            for (std::size_t i = 0; i < size; i++) {
                void* p1 = memory + i;
                ::new(p1) T();
            }
#endif
        }

        template<class T>
        inline void ms_init(T* memory, std::size_t size) {
            boost::has_trivial_default_constructor<T> trivial;
            ms_init(memory, size, trivial);
        }

        template<class T, std::size_t N>
        inline void ms_init(T* memory, std::size_t size, const T* list) {
#if !defined(BOOST_NO_EXCEPTIONS)
            std::size_t i = 0;
            try {
                for (; i < size; i++) {
                    void* p1 = memory + i;
                    ::new(p1) T(list[i % N]);
                }
            } catch (...) {
                ms_destroy(memory, i);
                throw;
            }
#else
            for (std::size_t i = 0; i < size; i++) {
                void* p1 = memory + i;
                ::new(p1) T(list[i % N]);
            }
#endif
        }

#if !defined(BOOST_NO_CXX11_ALLOCATOR)
        template<class T, class A>
        inline void as_destroy(const A& allocator, T* memory,
            std::size_t size) {
            typedef typename std::allocator_traits<A>::
                template rebind_alloc<T> TA;
            typedef typename std::allocator_traits<A>::
                template rebind_traits<T> TT;
            TA a2(allocator);
            for (std::size_t i = size; i > 0;) {
                TT::destroy(a2, &memory[--i]);
            }
        }

        template<class T, class A>
        inline void as_init(const A& allocator, T* memory, std::size_t size,
            ms_is_trivial) {
            typedef typename std::allocator_traits<A>::
                template rebind_alloc<T> TA;
            typedef typename std::allocator_traits<A>::
                template rebind_traits<T> TT;
            TA a2(allocator);
            for (std::size_t i = 0; i < size; i++) {
                TT::construct(a2, memory + i);
            }
        }

        template<class T, class A>
        inline void as_init(const A& allocator, T* memory, std::size_t size,
            ms_no_trivial) {
            typedef typename std::allocator_traits<A>::
                template rebind_alloc<T> TA;
            typedef typename std::allocator_traits<A>::
                template rebind_traits<T> TT;
            TA a2(allocator);
#if !defined(BOOST_NO_EXCEPTIONS)
            std::size_t i = 0;
            try {
                for (; i < size; i++) {
                    TT::construct(a2, memory + i);
                }
            } catch (...) {
                as_destroy(a2, memory, i);
                throw;
            }
#else
            for (std::size_t i = 0; i < size; i++) {
                TT::construct(a2, memory + i);
            }
#endif
        }

        template<class T, class A>
        inline void as_init(const A& allocator, T* memory, std::size_t size) {
            boost::has_trivial_default_constructor<T> trivial;
            as_init(allocator, memory, size, trivial);
        }

        template<class T, class A, std::size_t N>
        inline void as_init(const A& allocator, T* memory, std::size_t size,
            const T* list) {
            typedef typename std::allocator_traits<A>::
                template rebind_alloc<T> TA;
            typedef typename std::allocator_traits<A>::
                template rebind_traits<T> TT;
            TA a2(allocator);
#if !defined(BOOST_NO_EXCEPTIONS)
            std::size_t i = 0;
            try {
                for (; i < size; i++) {
                    TT::construct(a2, memory + i, list[i % N]);
                }
            } catch (...) {
                as_destroy(a2, memory, i);
                throw;
            }
#else
            for (std::size_t i = 0; i < size; i++) {
                TT::construct(a2, memory + i, list[i % N]);
            }
#endif
        }
#endif

        template<class T>
        inline void ms_noinit(T*, std::size_t, ms_is_trivial) {
        }

        template<class T>
        inline void ms_noinit(T* memory, std::size_t size, ms_no_trivial) {
#if !defined(BOOST_NO_EXCEPTIONS)
            std::size_t i = 0;
            try {
                for (; i < size; i++) {
                    void* p1 = memory + i;
                    ::new(p1) T;
                }
            } catch (...) {
                ms_destroy(memory, i);
                throw;
            }
#else
            for (std::size_t i = 0; i < size; i++) {
                void* p1 = memory + i;
                ::new(p1) T;
            }
#endif
        }

        template<class T>
        inline void ms_noinit(T* memory, std::size_t size) {
            boost::has_trivial_default_constructor<T> trivial;
            ms_noinit(memory, size, trivial);
        }
    }
}

#endif
