/***************************************************************************************************
 * Copyright 2025 NVIDIA Corporation. All rights reserved.
 **************************************************************************************************/
/// \file mi/base/interface_implement.h
/// \brief Mixin class template for deriving interface implementations.

#ifndef MI_BASE_INTERFACE_IMPLEMENT_H
#define MI_BASE_INTERFACE_IMPLEMENT_H

#include <mi/base/types.h>
#include <mi/base/uuid.h>
#include <mi/base/atom.h>

namespace mi {
namespace base {

/** \addtogroup mi_base_iinterface
@{
*/

// Forward declaration
class IInterface;

/// Mixin class template for deriving interface implementations.
///
/// #mi::base::Interface_implement is a mixin class template that allows you to derive
/// interface class implementations easily. It provides you with the full implementation
/// of reference counting and the #mi::base::IInterface::get_interface(const Uuid&)
/// method. It requires that you used interfaces derived from the corresponding mixin
/// class template #mi::base::Interface_declare.
///
/// #mi::base::Interface_implement is derived from the interface \c I.
///
/// \tparam I The interface class that this class implements.
///
///   \par Include File:
///    <tt> \#include <mi/base/interface_implement.h></tt>
///
template <class I>
class Interface_implement : public I
{
public:
    using Interface = I;

    /// Constructor.
    ///
    /// \param initial   The initial reference count (defaults to 1).
    Interface_implement( Uint32 initial=1)
        : m_refcnt( initial)
    {
    }

    /// Copy constructor.
    ///
    /// Initializes the reference count to 1.
    Interface_implement( const Interface_implement<I>& other)
        : m_refcnt( 1)
    {
        // avoid warning
        (void) other;
    }

    /// Assignment operator.
    ///
    /// The reference count of \c *this and \p other remain unchanged.
    Interface_implement<I>& operator=( const Interface_implement<I>& other)
    {
        // Note: no call of operator= on m_refcount
        // avoid warning
        (void) other;
        return *this;
    }

    /// Move constructor.
    Interface_implement(Interface_implement&& other)
        : m_refcnt{other.m_refcnt.swap(0)}
    {
    }

    /// Move assignment.
    Interface_implement& operator=(Interface_implement&& other)
    {
        other.m_refcnt = m_refcnt.swap(other.m_refcnt);
        return *this;
    }

    /// Increments the reference count.
    ///
    /// Increments the reference count of the object referenced through this interface
    /// and returns the new reference count. The operation is thread-safe.
    Uint32 retain() const override
    {
        return ++m_refcnt;
    }

    /// Decrements the reference count.
    ///
    /// Decrements the reference count of the object referenced through this interface
    /// and returns the new reference count. If the reference count dropped to
    /// zero, the object will be deleted. The operation is thread-safe.
    Uint32 release() const override
    {
        Uint32 cnt = --m_refcnt;
        if( !cnt)
            delete this;
        return cnt;
    }

    /// Acquires a const interface.
    ///
    /// If this interface is derived from or is the interface with the passed
    /// \p interface_id, then return a non-\c nullptr \c const #mi::base::IInterface* that
    /// can be casted via \c static_cast to an interface pointer of the interface type
    /// corresponding to the passed \p interface_id. Otherwise return \c nullptr.
    ///
    /// In the case of a non-\c nullptr return value, the caller receives ownership of the
    /// new interface pointer, whose reference count has been retained once. The caller
    /// must release the returned interface pointer at the end to prevent a memory leak.
    const IInterface* get_interface( const Uuid& interface_id) const override
    {
        return I::get_interface_static( this, interface_id);
    }

    /// Acquires a mutable interface.
    ///
    /// If this interface is derived from or is the interface with the passed
    /// \p interface_id, then return a non-\c nullptr #mi::base::IInterface* that
    /// can be casted via \c static_cast to an interface pointer of the interface type
    /// corresponding to the passed \p interface_id. Otherwise return \c nullptr.
    ///
    /// In the case of a non-\c nullptr return value, the caller receives ownership of the
    /// new interface pointer, whose reference count has been retained once. The caller
    /// must release the returned interface pointer at the end to prevent a memory leak.
    IInterface* get_interface( const Uuid& interface_id) override
    {
        return I::get_interface_static( this, interface_id);
    }

    using I::get_interface;

    /// Returns the interface ID of the most derived interface.
    Uuid get_iid() const override
    {
        return typename I::IID();
    }

protected:
    /// Get the current refcount.
    Atom32& refcount() const { return m_refcnt; }

protected:
    virtual ~Interface_implement() = default;

private:
    mutable Atom32 m_refcnt;
};


/// Mixin class template for deriving interface implementations from two interfaces.
///
/// #mi::base::Interface_implement_2 is a mixin class template that allows you to derive
/// interface class implementations easily. It provides you with the full implementation
/// of reference counting and the #mi::base::IInterface::get_interface(const Uuid&)
/// method. It requires that you used interfaces derived from the corresponding mixin
/// class template #mi::base::Interface_declare.
///
/// #mi::base::Interface_implement is derived from the interface \c I1 and \c I2.
/// In case of ambiguities, interface \c I1 is preferred.
///
/// \tparam I1 First  interface class that this class implements.
/// \tparam I2 Second interface class that this class implements.
///
///   \par Include File:
///    <tt> \#include <mi/base/interface_implement.h></tt>
///
template <class I1, class I2>
class Interface_implement_2 : public I1, public I2
{
public:
    /// Constructor.
    ///
    /// \param initial   The initial reference count (defaults to 1).
    Interface_implement_2( Uint32 initial=1)
        : m_refcnt( initial)
    {
    }

    /// Copy constructor.
    ///
    /// Initializes the reference count to 1.
    Interface_implement_2( const Interface_implement_2<I1,I2>& other)
        : m_refcnt( 1)
    {
        // avoid warning
        (void) other;
    }

    /// Assignment operator.
    ///
    /// The reference count of \c *this and \p other remain unchanged.
    Interface_implement_2<I1,I2>& operator=( const Interface_implement_2<I1,I2>& other)
    {
        // Note: no call of operator= on m_refcount
        // avoid warning
        (void) other;
        return *this;
    }

    /// Increments the reference count.
    ///
    /// Increments the reference count of the object referenced through this interface
    /// and returns the new reference count. The operation is thread-safe.
    virtual Uint32 retain() const
    {
        return ++m_refcnt;
    }

    /// Decrements the reference count.
    ///
    /// Decrements the reference count of the object referenced through this interface
    /// and returns the new reference count. If the reference count dropped to
    /// zero, the object will be deleted. The operation is thread-safe.
    virtual Uint32 release() const
    {
        Uint32 cnt = --m_refcnt;
        if( !cnt)
            delete this;
        return cnt;
    }

    /// Acquires a const interface.
    ///
    /// If this interface is derived from or is the interface with the passed
    /// \p interface_id, then return a non-\c nullptr \c const #mi::base::IInterface* that
    /// can be casted via \c static_cast to an interface pointer of the interface type
    /// corresponding to the passed \p interface_id. Otherwise return \c nullptr.
    ///
    /// In the case of a non-\c nullptr return value, the caller receives ownership of the
    /// new interface pointer, whose reference count has been retained once. The caller
    /// must release the returned interface pointer at the end to prevent a memory leak.
    virtual const IInterface* get_interface( const Uuid& interface_id) const
    {
        const IInterface* iptr = I1::get_interface_static(
            static_cast<const I1*>( this), interface_id);
        if( iptr)
            return iptr;

        return I2::get_interface_static( static_cast<const I2*>( this), interface_id);
    }

    /// Acquires a mutable interface.
    ///
    /// If this interface is derived from or is the interface with the passed
    /// \p interface_id, then return a non-\c nullptr #mi::base::IInterface* that
    /// can be casted via \c static_cast to an interface pointer of the interface type
    /// corresponding to the passed \p interface_id. Otherwise return \c nullptr.
    ///
    /// In the case of a non-\c nullptr return value, the caller receives ownership of the
    /// new interface pointer, whose reference count has been retained once. The caller
    /// must release the returned interface pointer at the end to prevent a memory leak.
    virtual IInterface* get_interface( const Uuid& interface_id)
    {
        IInterface* iptr = I1::get_interface_static( static_cast<I1*>( this), interface_id);
        if( iptr)
            return iptr;

        return I2::get_interface_static( static_cast<I2*>( this), interface_id);
    }

    using I1::get_interface;

    /// Returns the interface ID of the most derived interface.
    Uuid get_iid() const
    {
        return typename I1::IID();
    }

protected:
    virtual ~Interface_implement_2() = default;

private:
    mutable Atom32 m_refcnt;
};


/// Mixin class template for deriving singleton interface implementations,
/// where the reference count is fixed to one.
///
/// #mi::base::Interface_implement is a mixin class template that allows you to derive
/// interface class implementations easily. It provides you with a special implementation
/// of reference counting and the #mi::base::IInterface::get_interface(const Uuid&)
/// method. It requires that you used interfaces derived from the corresponding mixin
/// class template #mi::base::Interface_declare.
///
/// The reference counting is fixed to have a count of always one. It is suitable, for
/// example, for singleton objects that get once allocated and never deallocated.
///
/// #mi::base::Interface_implement is derived from the interface \c I.
///
/// \tparam I The interface class that this class implements.
///
template <class I>
class Interface_implement_singleton : public I
{
public:
    /// Returns the fixed reference count of one.
    ///
    /// Implements #mi::base::IInterface::retain() with a constant reference
    /// count of one.
    Uint32 retain() const override
    {
        return 1;
    }

    /// Returns the fixed reference count of one.
    ///
    /// Implements #mi::base::IInterface::release() with a constant reference
    /// count of one. The object will never be deleted through a release call.
    Uint32 release() const override
    {
        return 1;
    }

    /// Acquires a const interface.
    ///
    /// If this interface is derived from or is the interface with the passed
    /// \p interface_id, then return a non-\c nullptr \c const #mi::base::IInterface* that
    /// can be casted via \c static_cast to an interface pointer of the interface type
    /// corresponding to the passed \p interface_id. Otherwise return \c nullptr.
    ///
    /// In the case of a non-\c nullptr return value, the caller receives ownership of the
    /// new interface pointer, whose reference count has been retained once. The caller
    /// must release the returned interface pointer at the end to prevent a memory leak.
    const IInterface* get_interface( const Uuid& interface_id) const override
    {
        return I::get_interface_static( this, interface_id);
    }

    /// Acquires a mutable interface.
    ///
    /// If this interface is derived from or is the interface with the passed
    /// \p interface_id, then return a non-\c nullptr #mi::base::IInterface* that
    /// can be casted via \c static_cast to an interface pointer of the interface type
    /// corresponding to the passed \p interface_id. Otherwise return \c nullptr.
    ///
    /// In the case of a non-\c nullptr return value, the caller receives ownership of the
    /// new interface pointer, whose reference count has been retained once. The caller
    /// must release the returned interface pointer at the end to prevent a memory leak.
    IInterface* get_interface( const Uuid& interface_id) override
    {
        return I::get_interface_static( this, interface_id);
    }

    using I::get_interface;

    /// Returns the interface ID of the most derived interface.
    Uuid get_iid() const override
    {
        return typename I::IID();
    }

protected:
    virtual ~Interface_implement_singleton() = default;
};


/*@}*/ // end group mi_base_iinterface

} // namespace base
} // namespace mi

#endif // MI_BASE_INTERFACE_IMPLEMENT_H
