/*
 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that 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 .
 */
#ifndef TRINITY_MEMORY_H
#define TRINITY_MEMORY_H
#include "CompilerDefs.h"
#include 
#include 
#if TRINITY_COMPILER == TRINITY_COMPILER_GNU
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wignored-attributes"
#endif
namespace Trinity
{
namespace Impl
{
template
struct stateful_unique_ptr_deleter
{
    using pointer = T;
    explicit(false) stateful_unique_ptr_deleter(Del deleter) : _deleter(std::move(deleter)) { }
    void operator()(pointer ptr) const { (void)_deleter(ptr); }
private:
    Del _deleter;
};
template
struct stateless_unique_ptr_deleter
{
    using pointer = T;
    void operator()(pointer ptr) const
    {
        if constexpr (std::is_member_function_pointer_v)
            (void)(ptr->*Del)();
        else
            (void)Del(ptr);
    }
};
}
/**
 * Convenience function to construct type aliases for std::unique_ptr stateful deleters (such as lambda with captures)
 * @tparam Ptr    Type of the pointer
 * @tparam Del    Type of object freeing function (deduced from argument)
 * @param deleter Object deleter
 *
 * Example use
 * @code
 * void FreeV1(Resource*);
 * void FreeV2(Resource*);
 *
 * using ResourceDeleter = decltype(Trinity::unique_ptr_deleter(&FreeV1));
 *
 * std::unique_ptr resource = Trinity::make_unique_ptr_with_deleter(GetResourceV1(), &FreeV1);
 * // do stuff ...
 * // ... later get new resource
 * resource = Trinity::make_unique_ptr_with_deleter(GetResourceV2(), &FreeV2);
 * @endcode
 */
template  requires std::invocable && std::is_pointer_v
Impl::stateful_unique_ptr_deleter unique_ptr_deleter(Del deleter)
{
    return Impl::stateful_unique_ptr_deleter(std::move(deleter));
}
/**
 * Convenience function to construct type aliases for std::unique_ptr stateful deleters (such as lambda with captures)
 *
 * Main use is for forming struct/class members without the call to make_unique_ptr_with_deleter
 * @tparam Ptr    Type of the pointer
 * @tparam Del    The freeing function. This can be either a free function pointer that accepts T* as argument, pointer to a member function of T that accepts no arguments or a lambda with no captures
 *
 * Example use
 * @code
 * using FileDeleter = decltype(Trinity::unique_ptr_deleter());
 *
 * class Resource
 * {
 *     std::unique_ptr File;
 * };
 * @endcode
 */
template  requires std::invocable && std::is_pointer_v
Impl::stateless_unique_ptr_deleter unique_ptr_deleter()
{
    return Impl::stateless_unique_ptr_deleter();
}
/**
 * Utility function to construct a std::unique_ptr object with custom stateful deleter (such as lambda with captures)
 * @tparam Ptr    Type of the pointer
 * @tparam T      Type of the pointed-to object (defaults to std::remove_pointer_t)
 * @tparam Del    Type of object freeing function (deduced from second argument)
 * @param ptr     Raw pointer to owned object
 * @param deleter Object deleter
 *
 * Example use
 * @code
 * class Resource
 * {
 * };
 * class ResourceService
 * {
 *     Resource* Create();
 *     void Destroy(Resource*);
 * };
 *
 * ResourceService* service = GetResourceService();
 *
 * // Lambda that captures all required variables for destruction
 * auto resource = Trinity::make_unique_ptr_with_deleter(service->Create(), [service](Resource* res){ service->Destroy(res); });
 * @endcode
 */
template, typename Del> requires std::invocable && std::is_pointer_v
inline std::unique_ptr> make_unique_ptr_with_deleter(Ptr ptr, Del deleter)
{
    return std::unique_ptr>(ptr, std::move(deleter));
}
/**
 * Utility function to construct a std::unique_ptr object with custom stateless deleter (function pointer, captureless lambda)
 * @tparam Del    The freeing function. This can be either a free function pointer that accepts T* as argument, pointer to a member function of T that accepts no arguments or a lambda with no captures
 * @tparam Ptr    Type of the pointer
 * @tparam T      Type of the pointed-to object (defaults to std::remove_pointer_t)
 * @param ptr     Raw pointer to owned object
 *
 * Example uses
 * @code
 * void DestroyResource(Resource*);
 * class Resource
 * {
 *     void Destroy();
 * };
 *
 * // Free function
 * auto free = Trinity::make_unique_ptr_with_deleter<&DestroyResource>(new Resource());
 *
 * // Member function
 * auto member = Trinity::make_unique_ptr_with_deleter<&Resource::Destroy>(new Resource());
 *
 * // Lambda
 * auto lambda = Trinity::make_unique_ptr_with_deleter<[](Resource* res){ res->Destroy(); }>(new Resource());
 * @endcode
 */
template> requires std::invocable && std::is_pointer_v
inline std::unique_ptr> make_unique_ptr_with_deleter(Ptr ptr)
{
    return std::unique_ptr>(ptr);
}
}
#if TRINITY_COMPILER == TRINITY_COMPILER_GNU
#pragma GCC diagnostic pop
#endif
#endif // TRINITY_MEMORY_H