/*
 * Copyright (c) Atmosphère-NX
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
#pragma once
#include 
#include 
namespace ams::util {
    template
    class IFunction;
    namespace impl {
        template
        struct GetIFunctionTypeForObject;
        template
        struct GetIFunctionTypeForObject { using Type = R(Args...); };
        template
        struct GetIFunctionTypeForObject { using Type = R(Args...); };
        template
        struct GetIFunctionType;
        template
        struct GetIFunctionType { using Type = R(Args...); };
        template
        struct GetIFunctionType : public GetIFunctionType{};
        template
        struct GetIFunctionType> : public GetIFunctionType{};
        template
        struct GetIFunctionType : public GetIFunctionTypeForObject{};
        template
        class Function;
        template
        class Function::value && !std::is_final::value)>::type> final : public IFunction {
            private:
                F m_f;
            public:
                constexpr explicit Function(F f) : m_f(std::move(f)) { /* ... */}
                constexpr virtual ~Function() override { /* ... */ }
                constexpr virtual R operator()(Args... args) const override final {
                    return m_f(std::forward(args)...);
                }
        };
        template
        class Function::value && !std::is_final::value>::type> final : public IFunction, private F {
            public:
                constexpr explicit Function(F f) : F(std::move(f)) { /* ... */}
                constexpr virtual ~Function() override { /* ... */ }
                constexpr virtual R operator()(Args... args) const override final {
                    return static_cast(*this).operator()(std::forward(args)...);
                }
        };
        template
        constexpr ALWAYS_INLINE auto MakeIFunctionExplicitly(F f) {
            using FunctionType = ::ams::util::impl::Function::type>;
            return FunctionType{ std::move(f) };
        }
        template
        constexpr ALWAYS_INLINE auto MakeIFunctionExplicitly(R T::*f) {
            return MakeIFunctionExplicitly(std::mem_fn(f));
        }
    }
    template
    class IFunction {
        protected:
            constexpr virtual ~IFunction() { /* ... */ };
        public:
            constexpr virtual R operator()(Args... args) const = 0;
            template
            static constexpr ALWAYS_INLINE auto Make(F f) {
                return ::ams::util::impl::MakeIFunctionExplicitly(std::move(f));
            }
    };
    template::value>::type>
    constexpr ALWAYS_INLINE auto MakeIFunction(F f) {
        static_assert(!std::is_member_pointer::value);
        return IFunction::type>::Type>::Make(std::move(f));
    }
}