/************************************************************************
    created:    Tue Feb 28 2006
    authors:    Paul D Turner <paul@cegui.org.uk>
                Martin Preisler <martin@preisler.me>
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2014 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#ifndef _CEGUIFunctorCopySlot_h_
#define _CEGUIFunctorCopySlot_h_

#include "CEGUI/SlotFunctorBase.h"

// Start of CEGUI namespace section
namespace CEGUI
{

namespace FunctorCopySlot_detail
{
    typedef char Yes;
    typedef int No;

    template<typename T>
    struct HasBoolEventArgsConstOp
    {
        template<typename U, bool (U::*)(const EventArgs&) const> struct SFINAE {};
        template<typename U> static Yes Test(SFINAE<U, &U::operator()>*);
        template<typename U> static No Test(...);
        static const bool Value = sizeof(Test<T>(nullptr)) == sizeof(Yes);
    };

    template<typename T>
    struct HasBoolEventArgsOp
    {
        template<typename U, bool (U::*)(const EventArgs&)> struct SFINAE {};
        template<typename U> static Yes Test(SFINAE<U, &U::operator()>*);
        template<typename U> static No Test(...);
        static const bool Value = sizeof(Test<T>(nullptr)) == sizeof(Yes);
    };

    template<typename T>
    struct HasVoidEventArgsConstOp
    {
        template<typename U, void (U::*)(const EventArgs&) const> struct SFINAE {};
        template<typename U> static Yes Test(SFINAE<U, &U::operator()>*);
        template<typename U> static No Test(...);
        static const bool Value = sizeof(Test<T>(nullptr)) == sizeof(Yes);
    };

    template<typename T>
    struct HasVoidEventArgsOp
    {
        template<typename U, void (U::*)(const EventArgs&)> struct SFINAE {};
        template<typename U> static Yes Test(SFINAE<U, &U::operator()>*);
        template<typename U> static No Test(...);
        static const bool Value = sizeof(Test<T>(nullptr)) == sizeof(Yes);
    };

    template<typename T>
    struct HasBoolNoArgsConstOp
    {
        template<typename U, bool (U::*)() const> struct SFINAE {};
        template<typename U> static Yes Test(SFINAE<U, &U::operator()>*);
        template<typename U> static No Test(...);
        static const bool Value = sizeof(Test<T>(nullptr)) == sizeof(Yes);
    };

    template<typename T>
    struct HasBoolNoArgsOp
    {
        template<typename U, bool (U::*)()> struct SFINAE {};
        template<typename U> static Yes Test(SFINAE<U, &U::operator()>*);
        template<typename U> static No Test(...);
        static const bool Value = sizeof(Test<T>(nullptr)) == sizeof(Yes);
    };

    template<typename T>
    struct HasVoidNoArgsConstOp
    {
        template<typename U, void (U::*)() const> struct SFINAE {};
        template<typename U> static Yes Test(SFINAE<U, &U::operator()>*);
        template<typename U> static No Test(...);
        static const bool Value = sizeof(Test<T>(nullptr)) == sizeof(Yes);
    };

    // The following 3 templates use the same idea as boost::disable_if
    template <bool B, class T = void>
    struct DisableIfC
    {
        typedef T Type;
    };

    template <class T>
    struct DisableIfC<true, T>
    {};

    template <class Cond, class T = void>
    struct DisableIf : public DisableIfC<Cond::Value, T>
    {};


    // The following is just overload trickery to accommodate 8 allowed
    // operator() signatures:
    //
    // 1) bool (const EventArgs&) const = BoolEventArgsConstOp
    // 2) bool (const EventArgs&) = BoolEventArgsOp
    // 3) void (const EventArgs&) const = VoidEventArgsConstOp
    // 4) void (const EventArgs&) = VoidEventArgsOp
    // 5) bool () const = BoolNoArgsConstOp
    // 6) bool () = BoolNoArgsOp
    // 7) void () const = VoidNoArgsConstOp
    // 8) void () = VoidNoArgsOp
    //
    // The calls are priorised as outlined above, if call 1) is possible, no
    // other variants are tried, ...

    template<typename T>
    inline bool call(const T& functor, bool(T::*member_fn)(const EventArgs&) const, const EventArgs& args,
        void* = nullptr,
        void* = nullptr,
        void* = nullptr,
        void* = nullptr,
        void* = nullptr,
        void* = nullptr,
        void* = nullptr
    )
    {
        return (functor.*member_fn)(args);
    }

    template<typename T>
    inline bool call(T& functor, bool(T::*member_fn)(const EventArgs&), const EventArgs& args,
        typename DisableIf<HasBoolEventArgsConstOp<T> >::Type* = nullptr,
        void* = nullptr,
        void* = nullptr,
        void* = nullptr,
        void* = nullptr,
        void* = nullptr,
        void* = nullptr
    )
    {
        return (functor.*member_fn)(args);
    }

    template<typename T>
    inline bool call(const T& functor, void(T::*member_fn)(const EventArgs&) const, const EventArgs& args,
        typename DisableIf<HasBoolEventArgsConstOp<T> >::Type* = nullptr,
        typename DisableIf<HasBoolEventArgsOp<T> >::Type* = nullptr,
        void* = nullptr,
        void* = nullptr,
        void* = nullptr,
        void* = nullptr,
        void* = nullptr
    )
    {
        (functor.*member_fn)(args);
        return true;
    }

    template<typename T>
    inline bool call(T& functor, void(T::*member_fn)(const EventArgs&), const EventArgs& args,
        typename DisableIf<HasBoolEventArgsConstOp<T> >::Type* = nullptr,
        typename DisableIf<HasBoolEventArgsOp<T> >::Type* = nullptr,
        typename DisableIf<HasVoidEventArgsConstOp<T> >::Type* = nullptr,
        void* = nullptr,
        void* = nullptr,
        void* = nullptr,
        void* = nullptr
    )
    {
        (functor.*member_fn)(args);
        return true;
    }

    template<typename T>
    inline bool call(const T& functor, bool(T::*member_fn)() const, const EventArgs& /*args*/,
        typename DisableIf<HasBoolEventArgsConstOp<T> >::Type* = nullptr,
        typename DisableIf<HasBoolEventArgsOp<T> >::Type* = nullptr,
        typename DisableIf<HasVoidEventArgsConstOp<T> >::Type* = nullptr,
        typename DisableIf<HasVoidEventArgsOp<T> >::Type* = nullptr,
        void* = nullptr,
        void* = nullptr,
        void* = nullptr
    )
    {
        return (functor.*member_fn)();
    }

    template<typename T>
    inline bool call(T& functor, bool(T::*member_fn)(), const EventArgs& /*args*/,
        typename DisableIf<HasBoolEventArgsConstOp<T> >::Type* = nullptr,
        typename DisableIf<HasBoolEventArgsOp<T> >::Type* = nullptr,
        typename DisableIf<HasVoidEventArgsConstOp<T> >::Type* = nullptr,
        typename DisableIf<HasVoidEventArgsOp<T> >::Type* = nullptr,
        typename DisableIf<HasBoolNoArgsConstOp<T> >::Type* = nullptr,
        void* = nullptr,
        void* = nullptr
    )
    {
        return (functor.*member_fn)();
    }

    template<typename T>
    inline bool call(const T& functor, void(T::*member_fn)() const, const EventArgs& /*args*/,
        typename DisableIf<HasBoolEventArgsConstOp<T> >::Type* = nullptr,
        typename DisableIf<HasBoolEventArgsOp<T> >::Type* = nullptr,
        typename DisableIf<HasVoidEventArgsConstOp<T> >::Type* = nullptr,
        typename DisableIf<HasVoidEventArgsOp<T> >::Type* = nullptr,
        typename DisableIf<HasBoolNoArgsConstOp<T> >::Type* = nullptr,
        typename DisableIf<HasBoolNoArgsOp<T> >::Type* = nullptr,
        void* = nullptr
    )
    {
        (functor.*member_fn)();
        return true;
    }

    template<typename T>
    inline bool call(T& functor, void(T::*member_fn)(), const EventArgs& /*args*/,
        typename DisableIf<HasBoolEventArgsConstOp<T> >::Type* = nullptr,
        typename DisableIf<HasBoolEventArgsOp<T> >::Type* = nullptr,
        typename DisableIf<HasVoidEventArgsConstOp<T> >::Type* = nullptr,
        typename DisableIf<HasVoidEventArgsOp<T> >::Type* = nullptr,
        typename DisableIf<HasBoolNoArgsConstOp<T> >::Type* = nullptr,
        typename DisableIf<HasBoolNoArgsOp<T> >::Type* = nullptr,
        typename DisableIf<HasVoidNoArgsConstOp<T> >::Type* = nullptr
    )
    {
        (functor.*member_fn)();
        return true;
    }
}

/*!
\brief
    Slot template class that creates a functor that calls back via a copy of a
    functor object.
*/
template<typename T>
class FunctorCopySlot : public SlotFunctorBase<EventArgs>
{
public:
    FunctorCopySlot(const T& functor):
        d_functor(functor)
    {}

    bool operator()(const EventArgs& args) override
    {
        return FunctorCopySlot_detail::call<T>(d_functor, &T::operator(), args);
    }

private:
    T d_functor;
};

}

#endif  // end of guard _CEGUIFunctorCopySlot_h_
