// Copyright 2020 Siemens Digital Industries Software
// ==================================================
// Copyright 2017.
// Siemens Product Lifecycle Management Software Inc.
// All Rights Reserved.
// ==================================================
// Copyright 2020 Siemens Digital Industries Software

/**
    @file
    Template class that stores a pointer pointing to memory allocated using SM
    (Storage Management module from libsyss). 
    <br>It ensures that memory is freed automatically when an instance of this
    class goes out of scope.
*/

#ifndef TEAMCENTER_BASE_UTILS_SCOPED_SM_PTR_HXX
#define TEAMCENTER_BASE_UTILS_SCOPED_SM_PTR_HXX

#include <cstdlib>
/*  */
#include <base_utils/Mem.h>

namespace Teamcenter
{
/**
    The default deallocator function for scoped_smptr.
    <br>Use this as template when supplying custom deallocator.
*/
template < class T > struct scoped_smptr_my_deallocator
{
    void operator() (T* bucket) const { MEM_free(bucket); }
};
/**
   @brief  Template class that stores a pointer pointing to memory allocated using some SM
   (Storage Management module from NX) allocation API. It ensures that memory is freed automatically
   when the instance of this class goes out of scope.
   
   <h2> Things to be aware of when using scoped_smptr</h2>
   Allocation scheme:  scoped_smptr <b>requires that memory be allocated via SM </b> (Storage Management module
   from NX) API (e.g. MEM_alloc, etc.). <br>
   2) scoped_smptr cannot be used for a class which uses SM memory via 'operator new' override. <br>
   Though the memory may be freed OK, the destructor wont be called and may lead to issues. <br>
   <br>
   
   <h2> Example </h2>
   @code
   // example.
   
   // allocate some SM memory
   void allocateSomeSmMemory( int** ptr )
   {
       *ptr = (int*) MEM_alloc( 10 * sizeof( int ) );
   }
   
   void fooAcceptsIntPtr( const int* ptr )
   {
       [...]
   }

   void incorrectUsage()
   {
       int* ptr0 = 0;

       // scoped_smptr internally stores the value of supplied pointer and uses it for cleanup when it goes out of scope.
       // As the value of 'ptr0' is 0, the value stored internally by 'smptr' is 0.
       scoped_smptr< int > smptr( ptr0 );

       allocateSomeSmMemory( &ptr0 );      // 'smptr' is not aware about this allocation. This leads to a memory leak!
   }

   // scoped_ptr can be used just like a normal pointer.
   // It is best to avoid using two named variables (one for the normal pointer and other scoped_smptr) and
   // use the scoped_smptr just like normal pointer.
   void recommendedUsage()
   {
       scoped_smptr< int > smptr;
       allocatedDummy( &smptr );

       // use like a normal pointer.
       smptr[0] = 1;

       // pass around like a normal pointer.
       fooAcceptsIntPtr( smptr );
   }
   @endcode
*/

template <class T, class F=scoped_smptr_my_deallocator< T > > class scoped_smptr
{
public:
    /**
       Constructor.
       Note that no conversion will be allowed: the pointer needs to be of the specified type (T).
    */
    explicit scoped_smptr( T* b = 0 ) : bucket( b ) {}

    /**
        Default destructor. It also deletes the object to which it points.
    */
    virtual ~scoped_smptr() { F f; f( bucket ); }    /*  */


    /** 
        Operator =
        <br>If the scoped pointer was already assigned, the current assignment is removed (memory is freed) 
        and replaced with the requested pointed.
    */
    scoped_smptr &  operator=(T *  ptr) { F f; f(bucket); bucket = ptr; return *this; }

    /**
       Operator & 
     */
    T** operator&() { return &bucket; }

    /** 
       Operator * 
     */
    T& operator*() const { return *bucket; }

    /** 
       Operator ->
     */
    T* operator->() const { return bucket; }

    /** 
       Operator []
     */
    const T& operator[](std::size_t inx) const { return bucket[inx]; }
    
    /** 
       Operator []
     */
    T& operator[](std::size_t inx)       { return bucket[inx]; }
    
    /** 
        Operator []
    */
    const T& operator[](int inx)    const { return bucket[inx]; }
    
    /** 
        Operator []
    */
    T& operator[](int inx)          { return bucket[inx]; }
    
    /** 
        Operator []
    */
    const T& operator[](unsigned int inx)    const { return bucket[inx]; }
    
    /** 
        Operator []
    */
    T& operator[](unsigned int inx)          { return bucket[inx]; }

    /** 
        Operator []
    */
    const T& operator[](short inx) const { return bucket[inx]; }
    
    /** 
        Operator []
    */
    T& operator[](short inx) { return bucket[inx]; }
    
    /** 
        Operator []
    */
    const T& operator[](long inx)   const { return bucket[inx]; }
    
    /** 
        Operator []
    */
    T& operator[](long inx)         { return bucket[inx]; }

    /** 
        Safe getter of the string of elements of type T.
        
        It returns the memory pointed by this object, or a pointer to a default constructed element
        if the object does not point to any memory.
        <br>This is particularly useful if the object manages memory of a C-string, because it can 
        be used for a NULL-safe assignment to a std::string.
    */
    const T* getString() const { static const T s_empty = T(); return bucket ? bucket : &s_empty; }

    /**
       Returns the pointed memory.
    */
    T* get() const { return bucket; }

    /**
       Returns the memory that has been managed by the smart pointer, and ends it management of this memory.
    */
    T* release() { T* tmp = bucket; bucket = 0; return tmp; }

    /** Operator == */
    bool operator == (const T* t) const { return bucket == t; }

    /** Operator != */
    bool operator != (const T* t) const { return bucket != t; }

    /** Unary NOT operator */
    bool operator! () const
    {
        return bucket == 0;
    }

protected:
    /**
       The managed pointer.
    */
    T* bucket;

private:
    /**
       Prevention of usage of the copy constructor.
    */
    scoped_smptr( const scoped_smptr& );

    /**
       Prevention of usage of the operator =
    */
    scoped_smptr& operator = ( const scoped_smptr& );
};

}

#endif
