/***************************************************************************************************
 * Copyright 2023 NVIDIA Corporation. All rights reserved.
 **************************************************************************************************/
/// \file
/// \brief API component for creation, assignment, and cloning of instances of types.

#ifndef MI_NEURAYLIB_IFACTORY_H
#define MI_NEURAYLIB_IFACTORY_H

#include <mi/base/interface_declare.h>
#include <mi/neuraylib/idata.h>
#include <mi/neuraylib/type_traits.h>

namespace mi {

class IEnum_decl;
class IString;
class IStructure_decl;

namespace neuraylib {

class ITransaction;

/** \addtogroup mi_neuray_types
@{
*/

/// This API component allows the creation, assignment, and cloning of instances of types.
/// It also provides a comparison function for types.
///
/// Types are interfaces derived from #mi::IData. See \ref mi_neuray_types for an explanation
/// of the type system.
class IFactory : public
    mi::base::Interface_declare<0x8afad838,0xe597,0x4a81,0x92,0x34,0x51,0xfe,0xa4,0xff,0x04,0x31>
{
public:
    /// Creates an object of the type \p type_name.
    ///
    /// The arguments passed to this method are passed to the constructor of the created object.
    ///
    /// This factory allows the creation of instances of #mi::IData and derived interfaces.
    /// \ifnot DICE_API For other kind of objects like DB elements use
    /// #mi::neuraylib::ITransaction::create(). \endif It is not possible to create instances of
    /// #mi::IRef or collections of references. Use \ifnot DICE_API
    /// #mi::neuraylib::ITransaction::create() \else #mi::neuraylib::IDice_transaction \endif
    /// instead.
    ///
    /// The created object will be initialized in a manner dependent upon the passed type
    /// name. Each class has its own policy on initialization. So, one should not make any
    /// assumptions on the values of the various class members.
    ///
    /// \ifnot DICE_API \see #mi::neuraylib::ITransaction::create() \endif
    ///
    /// \param type_name    The type name of the object to create. See \ref mi_neuray_types for
    ///                     possible type names.
    /// \param argc         The number of elements in \p argv.
    /// \param argv         The array of arguments passed to the constructor.
    /// \return             A pointer to the created object on success,
    ///                     or \c NULL on failure (e.g., invalid type name).
    virtual base::IInterface* create(
        const char* type_name,
        Uint32 argc = 0,
        const base::IInterface* argv[] = 0) = 0;

    /// Creates an object of the type \p type_name.
    ///
    /// The arguments passed to this method are passed to the constructor of the created object.
    ///
    /// This factory allows the creation of instances of #mi::IData and derived interfaces.
    /// \ifnot DICE_API For other kind of objects like DB elements use
    /// #mi::neuraylib::ITransaction::create(). \endif It is not possible to create instances of
    /// #mi::IRef or collections of references. Use \ifnot DICE_API
    /// #mi::neuraylib::ITransaction::create() \else #mi::neuraylib::IDice_transaction \endif
    /// instead.
    ///
    /// The created object will be initialized in a manner dependent upon the passed type
    /// name. Each class has its own policy on initialization. So, one should not make any
    /// assumptions on the values of the various class members.
    ///
    /// Note that there are two versions of this templated member function, one that takes no
    /// arguments, and another one that takes one or three arguments (the type name, and two
    /// optional arguments passed to the factory class). The version with no arguments can only be
    /// used to create a subset of supported types: it supports only those types where the type name
    /// can be deduced from the template parameter, i.e., it does not support arrays, structures,
    /// maps, and pointers. The version with one or three arguments can be used to create any type
    /// (but requires the type name as parameter, which is redundant for many types). Attempts to
    /// use the version with no arguments with a template parameter where the type name can not be
    /// deduced results in compiler errors.
    ///
    /// This templated member function is a wrapper of the non-template variant for the user's
    /// convenience. It eliminates the need to call
    /// #mi::base::IInterface::get_interface(const Uuid&)
    /// on the returned pointer, since the return type already is a pointer to the type \p T
    /// specified as template parameter.
    ///
    /// \ifnot DICE_API \see #mi::neuraylib::ITransaction::create() \endif
    ///
    /// \param type_name    The type name of the object to create
    /// \param argc         The number of elements in \p argv
    /// \param argv         The array of arguments passed to the constructor
    /// \tparam T           The interface type of the class to create
    /// \return             A pointer to the created object on success,
    ///                     or \c NULL on failure (e.g., invalid type name).
    template<class T>
    T* create(
        const char* type_name,
        Uint32 argc = 0,
        const base::IInterface* argv[] = 0)
    {
        base::IInterface* ptr_iinterface = create( type_name, argc, argv);
        if ( !ptr_iinterface)
            return 0;
        T* ptr_T = static_cast<T*>( ptr_iinterface->get_interface( typename T::IID()));
        ptr_iinterface->release();
        return ptr_T;
    }

    /// Creates an object of the type \p T.
    ///
    /// This factory allows the creation of instances of #mi::IData and derived interfaces.
    /// \ifnot DICE_API For other kind of objects like DB elements use
    /// #mi::neuraylib::ITransaction::create(). \endif It is not possible to create instances of
    /// #mi::IRef or collections of references. Use \ifnot DICE_API
    /// #mi::neuraylib::ITransaction::create() \else #mi::neuraylib::IDice_transaction \endif
    /// instead.
    ///
    /// The created object will be initialized in a manner dependent upon the passed type
    /// name. Each class has its own policy on initialization. So, one should not make any
    /// assumptions on the values of the various class members.
    ///
    /// Note that there are two versions of this templated member function, one that takes no
    /// arguments, and another one that takes one or three arguments (the type name, and two
    /// optional arguments passed to the factory class). The version with no arguments can only be
    /// used to create a subset of supported types: it supports only those types where the type name
    /// can be deduced from the template parameter, i.e., it does not support arrays, structures,
    /// maps, and pointers. The version with one or three arguments can be used to create any type
    /// (but requires the type name as parameter, which is redundant for many types). Attempts to
    /// use the version with no arguments with a template parameter where the type name can not be
    /// deduced results in compiler errors.
    ///
    /// This templated member function is a wrapper of the non-template variant for the user's
    /// convenience. It eliminates the need to call
    /// #mi::base::IInterface::get_interface(const Uuid&)
    /// on the returned pointer, since the return type already is a pointer to the type \p T
    /// specified as template parameter.
    ///
    /// \ifnot DICE_API \see #mi::neuraylib::ITransaction::create() \endif
    ///
    /// \tparam T           The interface type of the class to create
    /// \return             A pointer to the created object on success,
    ///                     or \c NULL on failure (e.g., invalid type name).
    template<class T>
    T* create()
    {
        return create<T>( Type_traits<T>::get_type_name());
    }

    /// This enum represents possible events that can happen during assignment of types.
    ///
    /// The enum values are powers of two such that they can be combined in a bitmask. Such a
    /// bitmask is returned by #assign_from_to().
    enum Assign_result
    {
        /// One of the arguments \p source or \p target is \c NULL. Alternatively, a deep assignment
        /// to an instance of #mi::IPointer failed due to a \c NULL value.
        NULL_POINTER                     =  1,

        /// There is a structural mismatch between \p source and \p target.
        /// One of the two arguments is of type #mi::IData_simple and the other one is of
        /// #mi::IData_collection, or alternatively, both arguments are of type
        /// #mi::IData_collection and there is at least one key which is of type #mi::IData_simple
        /// in one argument, and of type #mi::IData_collection in the other argument.
        STRUCTURAL_MISMATCH              =  2,

        /// Both arguments (or at least one key thereof) are of different interfaces derived from
        /// #mi::IData_simple and there is no conversion between them. This happens for example
        /// if an instance of #mi::ISint32 is assigned to an instance of #mi::IString).
        NO_CONVERSION                    =  4,

        /// There is at least one key in \p source which does not exist in \p target.
        TARGET_KEY_MISSING               =  8,

        /// There is at least one key in \p target which does not exist in \p source.
        SOURCE_KEY_MISSING               = 16,

        /// Both arguments \p source and \p target (or the types of at least one key thereof) are
        /// collections, but of different type. For example, this flag is set when assigning from
        /// static to dynamic arrays, from arrays to maps, or from arrays or maps to compounds, or
        /// vice versa.
        DIFFERENT_COLLECTIONS            = 32,

        /// The argument \p source is a collection and contains at least one key which is not of
        /// type #mi::IData, e.g., in untyped arrays or maps. Or \p source is a pointer or contains
        /// a key that is a pointer that wraps an instance not derived from #mi::IData. Such types
        /// cannot get assigned with this method.
        NON_IDATA_VALUES                 = 64,

        /// Shallow assignment (the default) was requested and the assignment failed due to
        /// incompatible pointer types, e.g., assigning from #mi::IConst_pointer to
        /// #mi::IPointer, or between different typed pointers (#mi::IPointer::set_pointer() or
        /// #mi::IConst_pointer::set_pointer() failed).
        INCOMPATIBLE_POINTER_TYPES       = 128,

        /// Deep assignment was requested and \p target is an instance of #mi::IConst_pointer (or
        /// a key of \p target is an instance of #mi::IConst_pointer).
        DEEP_ASSIGNMENT_TO_CONST_POINTER = 256,

        /// The assignment failed due to incompatible privacy levels, i.e.,
        /// #mi::IRef::set_reference() returned error code -4.
        INCOMPATIBLE_PRIVACY_LEVELS      = 1024,

        /// The assignment failed due to incompatible enum types.
        INCOMPATIBLE_ENUM_TYPES          = 2048,

        /// The assignment failed due to incompatible options.
        INCOMPATIBLE_OPTIONS             = 4096,

        //  Undocumented, for alignment only
        FORCE_32_BIT_RESULT              = 0xffffffffU
    };

    /// This enum represents various options for the assignment or cloning of types.
    ///
    /// The enum values are powers of two such that they can be combined in a bitmask. Such a
    /// bitmask van be is passed to #assign_from_to() and #clone().
    enum Assign_clone_options
    {
        /// By default, assignment or cloning of instances of #mi::IPointer and #mi::IConst_pointer
        /// is shallow, i.e., the pointer represented by these interfaces is assigned or cloned as a
        /// plain value and both pointers point to the same interface afterwards. If this option is
        /// given the assignment or cloning is deep, i.e., the operations happens on the wrapped
        /// pointers themselves.
        DEEP_ASSIGNMENT_OR_CLONE = 1,

        /// By default, assignment might change the set of keys if the for target is a dynamic array
        /// or map.  If this option is given the set of keys in the target remains fixed.
        FIX_SET_OF_TARGET_KEYS = 4,

        //  Undocumented, for alignment only
        FORCE_32_BIT_OPTIONS     = 0xffffffffU
    };

    /// Assigns the value(s) of \p source to \p target.
    ///
    /// Assignment succeeds if both \p source and \p target are of the same type. If not, the
    /// method tries to perform an assignment on a best effort basis. For example, an assignment
    /// between interfaces derived from #mi::INumber might require a conversion as defined in the
    /// C/C++ standard. If both arguments are of type #mi::IData_collection, assignment happens
    /// for all keys existing in both parameters.
    ///
    /// \param source   The instance to assign from.
    /// \param target   The instance to assign to.
    /// \param options  See #Assign_clone_options for possible options.
    /// \return         The return value is a bit field that indicates which events occurred during
    ///                 assignment. See #Assign_result for possible events.
    ///                 \par
    ///                 The value 0 indicates that the assignment took place without any problems.
    ///                 Note that a non-zero value is not necessarily an error, it is up to the
    ///                 caller to interpret the result according to the context. For example,
    ///                 assigning from an instance of #mi::IFloat32_3 to an instance of
    ///                 #mi::IFloat32_2 will result in #TARGET_KEY_MISSING because there is no
    ///                 key named \c "z" in #mi::IFloat32_2. It is up to the caller to decide
    ///                 whether this is the intended behavior or not.
    virtual Uint32 assign_from_to( const IData* source, IData* target, Uint32 options = 0) = 0;

    /// Creates a clone of a type.
    ///
    /// It is not possible to clone untyped pointers and collections that wrap/contain interface
    /// pointers that are not of type #mi::IData.
    ///
    /// \param source   The instance to clone.
    /// \param options  See #Assign_clone_options for possible options.
    virtual IData* clone( const IData* source, Uint32 options = 0) = 0;

    /// Creates a clone of a type.
    ///
    /// It is not possible to clone untyped pointers and collections that wrap/contain interface
    /// pointers that are not of type #mi::IData.
    ///
    /// This templated member function is a wrapper of the non-template variant for the user's
    /// convenience. It eliminates the need to call
    /// #mi::base::IInterface::get_interface(const Uuid&)
    /// on the returned pointer, since the return type already is a pointer to the type \p T
    /// specified as template parameter.
    ///
    /// \param source   The instance to clone.
    /// \param options  See #Assign_clone_options for possible options.
    /// \tparam T       The interface type of the class to create
    template<class T>
    T* clone( const IData* source, Uint32 options = 0)
    {
        mi::base::IInterface* ptr_iinterface = clone( source, options);
        if ( !ptr_iinterface)
            return 0;
        T* ptr_T = static_cast<T*>( ptr_iinterface->get_interface( typename T::IID()));
        ptr_iinterface->release();
        return ptr_T;
    }

    /// Compares two instances of #mi::IData.
    ///
    /// The comparison operator for instances of #mi::IData is defined as follows:
    /// - If \p lhs or \p rhs is \c NULL, the result is the lexicographic comparison of
    ///   the pointer addresses themselves.
    /// - Otherwise, the type names of \p lhs and \p rhs are considered. If they are different,
    ///   the result is determined by \c strcmp() on the type names. For example, this method
    ///   never returns 0 if you compare a dynamic array and a static array, even if they have
    ///   the same element type, length, and equal element values.
    /// - Finally, the values of the elements are compared lexicographically, as defined by
    ///   \c operator< or \c strcmp() with the following special cases for selected interfaces:
    ///   - #mi::IRef: If #mi::IRef::get_reference_name() returns \c NULL for at least one operand,
    ///     the result is the lexicographic comparison of the pointer addresses. Otherwise, the
    ///     result is determined by \c strcmp() on the names of the referenced DB elements.
    ///   - #mi::IPointer and #mi::IConst_pointer: If #mi::IPointer::get_pointer() or
    ///     #mi::IConst_pointer::get_pointer() returns \c NULL for at least one operand, the result
    ///     is the lexicographic comparison of the pointer addresses. Otherwise, if at least one
    ///     pointer is not of the type #mi::IData, the result is the lexicographic comparison of the
    ///     pointer addresses. Finally (both pointers are of type #mi::IData), the result is
    ///     determined by the recursive invocation of this method on these pointers.
    ///   - #mi::IData_collection: First, if the collections have different length, this decides
    ///     the comparison. Next, the elements are compared pairwise in lexicographic order of
    ///     the element index. If the element keys are different, \c strcmp() on the element keys
    ///     determines the result. Otherwise, the element values are compared by recursive
    ///     invocation of this method (or lexicographic comparison of the pointer addresses if one
    ///     element is not of type #mi::IData). If they are different, this decides the result,
    ///     otherwise the comparison continues with the next element. If all elements are equal,
    ///     the result is 0.
    ///
    /// \param lhs   The left-hand side operand for the comparison.
    /// \param rhs   The right-hand side operand for the comparison.
    /// \return      -1 if \c lhs < \c rhs, 0 if \c lhs == \c rhs, and +1 if \c lhs > \c rhs.
    virtual Sint32 compare( const IData* lhs, const IData* rhs) = 0;

    /// Returns a textual representation of a type.
    ///
    /// The textual representation is of the form "type name = value" if \p name is not \c NULL,
    /// and of the form "value" if \p name is \c NULL. The representation of the value might
    /// contain line breaks, for example for structures and arrays. Subsequent lines have a
    /// suitable indentation. The assumed indentation level of the first line is specified by
    /// \p depth.
    ///
    /// \ifnot DICE_API
    /// \see #mi::neuraylib::IFactory::dump(ITransaction*,const IData*,const char*,Size)
    /// \endif
    virtual const IString* dump( const IData* data, const char* name = 0, Size depth = 0) = 0;

    /// Returns a textual representation of a type.
    ///
    /// This overload of #mi::neuraylib::IFactory::dump(const IData*,const char*,Size) requires an
    /// additional transaction pointer. This is necessary to support the dumping of possibly nested
    /// non-#mi::IData types.
    virtual const IString* dump(
        neuraylib::ITransaction* transaction,
        const IData* data,
        const char* name = 0,
        Size depth = 0) = 0;

    /// Returns a registered structure declaration.
    ///
    /// \param structure_name   The name of the structure declaration to return.
    /// \return                 The structure declaration for \p name, or \c NULL if there is no
    ///                         structure declaration for that name.
    virtual const IStructure_decl* get_structure_decl( const char* structure_name) const = 0;

    /// Returns a registered enum declaration.
    ///
    /// \param enum_name        The name of the enum declaration to return.
    /// \return                 The enum declaration for \p name, or \c NULL if there is no
    ///                         enum declaration for that name.
    virtual const IEnum_decl* get_enum_decl( const char* enum_name) const = 0;
};

mi_static_assert( sizeof( IFactory::Assign_result) == sizeof( Uint32));
mi_static_assert( sizeof( IFactory::Assign_clone_options) == sizeof( Uint32));

/**@}*/ // end group mi_neuray_types

} // namespace neuraylib

} // namespace mi

#endif // MI_NEURAYLIB_IFACTORY_H
