// https://github.com/vinniefalco/LuaBridge
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// Copyright 2007, Nathan Reed
// SPDX-License-Identifier: MIT

#pragma once

namespace luabridge {

namespace detail {

/*
 * Constructor generators.  These templates allow you to call operator new and
 * pass the contents of a type/value list to the Constructor.  Like the
 * function pointer containers, these are only defined up to 8 parameters.
 */

/** Constructor generators.

    These templates call operator new with the contents of a type/value
    list passed to the Constructor with up to 8 parameters. Two versions
    of call() are provided. One performs a regular new, the other performs
    a placement new.
*/
template<class T, typename List>
struct Constructor
{
};

template<class T>
struct Constructor<T, None>
{
    static T* call(TypeListValues<None> const&) { return new T; }
    static T* call(void* mem, TypeListValues<None> const&) { return new (mem) T; }
};

template<class T, class P1>
struct Constructor<T, TypeList<P1>>
{
    static T* call(const TypeListValues<TypeList<P1>>& tvl) { return new T(tvl.hd); }
    static T* call(void* mem, const TypeListValues<TypeList<P1>>& tvl)
    {
        return new (mem) T(tvl.hd);
    }
};

template<class T, class P1, class P2>
struct Constructor<T, TypeList<P1, TypeList<P2>>>
{
    static T* call(const TypeListValues<TypeList<P1, TypeList<P2>>>& tvl)
    {
        return new T(tvl.hd, tvl.tl.hd);
    }
    static T* call(void* mem, const TypeListValues<TypeList<P1, TypeList<P2>>>& tvl)
    {
        return new (mem) T(tvl.hd, tvl.tl.hd);
    }
};

template<class T, class P1, class P2, class P3>
struct Constructor<T, TypeList<P1, TypeList<P2, TypeList<P3>>>>
{
    static T* call(const TypeListValues<TypeList<P1, TypeList<P2, TypeList<P3>>>>& tvl)
    {
        return new T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd);
    }
    static T* call(void* mem, const TypeListValues<TypeList<P1, TypeList<P2, TypeList<P3>>>>& tvl)
    {
        return new (mem) T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd);
    }
};

template<class T, class P1, class P2, class P3, class P4>
struct Constructor<T, TypeList<P1, TypeList<P2, TypeList<P3, TypeList<P4>>>>>
{
    static T*
    call(const TypeListValues<TypeList<P1, TypeList<P2, TypeList<P3, TypeList<P4>>>>>& tvl)
    {
        return new T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd);
    }
    static T*
    call(void* mem,
         const TypeListValues<TypeList<P1, TypeList<P2, TypeList<P3, TypeList<P4>>>>>& tvl)
    {
        return new (mem) T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd);
    }
};

template<class T, class P1, class P2, class P3, class P4, class P5>
struct Constructor<T, TypeList<P1, TypeList<P2, TypeList<P3, TypeList<P4, TypeList<P5>>>>>>
{
    static T*
    call(const TypeListValues<TypeList<P1, TypeList<P2, TypeList<P3, TypeList<P4, TypeList<P5>>>>>>&
             tvl)
    {
        return new T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd);
    }
    static T*
    call(void* mem,
         const TypeListValues<TypeList<P1, TypeList<P2, TypeList<P3, TypeList<P4, TypeList<P5>>>>>>&
             tvl)
    {
        return new (mem) T(tvl.hd, tvl.tl.hd, tvl.tl.tl.hd, tvl.tl.tl.tl.hd, tvl.tl.tl.tl.tl.hd);
    }
};

template<class T, class P1, class P2, class P3, class P4, class P5, class P6>
struct Constructor<
    T,
    TypeList<P1, TypeList<P2, TypeList<P3, TypeList<P4, TypeList<P5, TypeList<P6>>>>>>>
{
    static T*
    call(const TypeListValues<
         TypeList<P1, TypeList<P2, TypeList<P3, TypeList<P4, TypeList<P5, TypeList<P6>>>>>>>& tvl)
    {
        return new T(tvl.hd,
                     tvl.tl.hd,
                     tvl.tl.tl.hd,
                     tvl.tl.tl.tl.hd,
                     tvl.tl.tl.tl.tl.hd,
                     tvl.tl.tl.tl.tl.tl.hd);
    }
    static T*
    call(void* mem,
         const TypeListValues<
             TypeList<P1, TypeList<P2, TypeList<P3, TypeList<P4, TypeList<P5, TypeList<P6>>>>>>>&
             tvl)
    {
        return new (mem) T(tvl.hd,
                           tvl.tl.hd,
                           tvl.tl.tl.hd,
                           tvl.tl.tl.tl.hd,
                           tvl.tl.tl.tl.tl.hd,
                           tvl.tl.tl.tl.tl.tl.hd);
    }
};

template<class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7>
struct Constructor<
    T,
    TypeList<P1,
             TypeList<P2, TypeList<P3, TypeList<P4, TypeList<P5, TypeList<P6, TypeList<P7>>>>>>>>
{
    static T*
    call(const TypeListValues<TypeList<
             P1,
             TypeList<P2, TypeList<P3, TypeList<P4, TypeList<P5, TypeList<P6, TypeList<P7>>>>>>>>&
             tvl)
    {
        return new T(tvl.hd,
                     tvl.tl.hd,
                     tvl.tl.tl.hd,
                     tvl.tl.tl.tl.hd,
                     tvl.tl.tl.tl.tl.hd,
                     tvl.tl.tl.tl.tl.tl.hd,
                     tvl.tl.tl.tl.tl.tl.tl.hd);
    }
    static T*
    call(void* mem,
         const TypeListValues<TypeList<
             P1,
             TypeList<P2, TypeList<P3, TypeList<P4, TypeList<P5, TypeList<P6, TypeList<P7>>>>>>>>&
             tvl)
    {
        return new (mem) T(tvl.hd,
                           tvl.tl.hd,
                           tvl.tl.tl.hd,
                           tvl.tl.tl.tl.hd,
                           tvl.tl.tl.tl.tl.hd,
                           tvl.tl.tl.tl.tl.tl.hd,
                           tvl.tl.tl.tl.tl.tl.tl.hd);
    }
};

template<class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8>
struct Constructor<
    T,
    TypeList<
        P1,
        TypeList<
            P2,
            TypeList<P3, TypeList<P4, TypeList<P5, TypeList<P6, TypeList<P7, TypeList<P8>>>>>>>>>
{
    static T*
    call(const TypeListValues<TypeList<
             P1,
             TypeList<
                 P2,
                 TypeList<P3,
                          TypeList<P4, TypeList<P5, TypeList<P6, TypeList<P7, TypeList<P8>>>>>>>>>&
             tvl)
    {
        return new T(tvl.hd,
                     tvl.tl.hd,
                     tvl.tl.tl.hd,
                     tvl.tl.tl.tl.hd,
                     tvl.tl.tl.tl.tl.hd,
                     tvl.tl.tl.tl.tl.tl.hd,
                     tvl.tl.tl.tl.tl.tl.tl.hd,
                     tvl.tl.tl.tl.tl.tl.tl.tl.hd);
    }
    static T*
    call(void* mem,
         const TypeListValues<TypeList<
             P1,
             TypeList<
                 P2,
                 TypeList<P3,
                          TypeList<P4, TypeList<P5, TypeList<P6, TypeList<P7, TypeList<P8>>>>>>>>>&
             tvl)
    {
        return new (mem) T(tvl.hd,
                           tvl.tl.hd,
                           tvl.tl.tl.hd,
                           tvl.tl.tl.tl.hd,
                           tvl.tl.tl.tl.tl.hd,
                           tvl.tl.tl.tl.tl.tl.hd,
                           tvl.tl.tl.tl.tl.tl.tl.hd,
                           tvl.tl.tl.tl.tl.tl.tl.tl.hd);
    }
};

} // namespace detail

} // namespace luabridge
