/***************************************************************************************************
 * Copyright 2020 NVIDIA Corporation. All rights reserved.
 **************************************************************************************************/
/// \file
/// \brief Abstract interface for user-defined classes.

#ifndef MI_NEURAYLIB_IUSER_CLASS_H
#define MI_NEURAYLIB_IUSER_CLASS_H

#include <mi/base/interface_declare.h>
#include <mi/base/interface_implement.h>
#include <mi/base/lock.h>
#include <mi/neuraylib/iserializer.h>

namespace mi {

class IArray;

namespace neuraylib {

class IDeserializer;
class ITransaction;

/** \addtogroup mi_neuray_plugins
@{
*/

/// Abstract interface for user-defined classes.
///
/// All user-defined classes have to be derived from this interface.
/// The mixing #mi::neuraylib::User_class is helpful to implement this interface (or interfaces
/// derived from it). User-defined classes have to be registered via
/// #mi::neuraylib::IExtension_api::register_class().
class IUser_class : public
    mi::base::Interface_declare<0xa8bbfac5,0xf1b0,0x4ab5,0x99,0x18,0x9a,0x46,0xf8,0xb8,0x32,0x2b,
                                neuraylib::ISerializable>
{
public:
    /// Creates a copy of the database element.
    ///
    /// Duplicating a database element is used by the database when it needs to create a new version
    /// of the given element, e.g., when someone edits an existing element. This member function
    /// must create and return a full copy of the element.
    ///
    /// \return A newly allocated instance of the database element. The pointer represents a
    ///         complete replica of the database element on which the member function was called
    ///         and is owned by the database.
    virtual IUser_class* copy() const = 0;

    /// Returns a human readable identifier for the class this database element belongs to.
    ///
    /// This name is \em not required for proper operation, but can be useful for debugging. For
    /// example, the name is used to display the class name in the tag table of the HTTP
    /// administration console.
    ///
    /// \return         The class name.
    virtual const char* get_class_name() const = 0;

    /// Returns the list of elements referenced by this element.
    ///
    /// The database ensures that elements will not be removed from the database as long as they
    /// are referenced by other elements, even if they have been scheduled for removal
    /// by \ifnot DICE_API #mi::neuraylib::ITransaction::remove(). \else
    /// #mi::neuraylib::IDice_transaction::remove(). \endif
    ///
    /// \param transaction   A transaction that can be used to create the return value or temporary
    ///                      values.
    /// \return              An array of strings, where each string is the name of a referenced
    ///                      element. Can be \c NULL if the element does not refer other elements.
    virtual IArray* get_references( ITransaction* transaction) const = 0;

    //  Sets the embedded pointer.
    //
    //  The embedded pointer is used for internal purposes. Users must not use this method.
    virtual bool set_pointer( const base::IInterface* pointer) = 0;

    //  Returns the embedded pointer.
    //
    //  The embedded pointer is used for internal purposes. Users must not use this method.
    virtual const base::IInterface* get_pointer() const = 0;
};

/// This mixin class should be used to implement the \c %IUser_class interface.
///
/// This interface provides a default implementation of some of the pure virtual methods of the
/// #mi::neuraylib::IUser_class interface.
template <Uint32 id1, Uint16 id2, Uint16 id3
    , Uint8 id4, Uint8 id5, Uint8 id6, Uint8 id7
    , Uint8 id8, Uint8 id9, Uint8 id10, Uint8 id11
    , class I = IUser_class>
class User_class : public base::Interface_implement<I>
{
public:
    /// Declares the class ID
    typedef base::Uuid_t<id1,id2,id3,id4,id5,id6,id7,id8,id9,id10,id11> IID;

    /// Default constructor
    User_class() : m_pointer( 0) { }

    /// Copy constructor
    User_class( const User_class& other) : base::Interface_implement<I>( other), m_pointer( 0) { }

    /// Assignment operator
    User_class& operator=( const User_class& other)
    {
        base::Interface_implement<I>::operator=( other);
        return *this;
    }

    /// Destructor
    ~User_class()
    {
        mi_base_assert( m_pointer == 0);
    }

    /// Returns a human readable class name
    virtual const char* get_class_name() const
    {
        return "User class";
    }

    /// Returns the class ID corresponding to the template parameters of this mixin class.
    virtual base::Uuid get_class_id() const
    {
        return IID();
    }

    //  Overrides the standard release() implementation.
    //
    //  If the release count drops to 1, and the embedded pointer is set, release it.
    virtual Uint32 release() const
    {
        base::Lock::Block block( &m_pointer_lock);
        base::Interface_implement<I>::retain();
        Uint32 count = base::Interface_implement<I>::release();
        if( count == 1) {
            block.release();
            return base::Interface_implement<I>::release();
        }
        if(( count == 2) && m_pointer) {
            m_pointer->release();
            m_pointer = 0;
        }
        return base::Interface_implement<I>::release();
    }

    //  Sets the embedded pointer.
    //
    //  The embedded pointer is used for internal purposes. Users must not use this method.
    virtual bool set_pointer( const base::IInterface* pointer)
    {
        base::Lock::Block block( &m_pointer_lock);
        if( m_pointer)
            return false;
        m_pointer = pointer;
        if( m_pointer)
            m_pointer->retain();
        return true;
    }

    //  Returns the embedded pointer.
    //
    //  The embedded pointer is used for internal purposes. Users must not use this method.
    virtual const base::IInterface* get_pointer() const
    {
        base::Lock::Block block( &m_pointer_lock);
        if( m_pointer)
            m_pointer->retain();
        return m_pointer;
    }

private:
    //  The embedded pointer.
    //
    //  The embedded pointer is used for internal purposes. Users must not access the pointer.
    mutable const base::IInterface* m_pointer;

    //  The lock that protects the embedded pointer.
    mutable base::Lock m_pointer_lock;
};

/*@}*/ // end group mi_neuray_plugins

} // namespace neuraylib

} // namespace mi

#endif // MI_NEURAYLIB_IUSER_CLASS_H
