/**
 * @file      lib.UniquePointer.hpp
 * @author    Sergey Baigudin, sergey@baigudin.software
 * @copyright 2022, Sergey Baigudin, Baigudin Software
 */
#ifndef LIB_UNIQUEPOINTER_HPP_
#define LIB_UNIQUEPOINTER_HPP_

#include "lib.Object.hpp"
#include "api.SmartPointer.hpp"
#include "lib.SmartPointerDeleter.hpp"

namespace eoos
{
namespace lib
{

#ifdef EOOS_ENABLE_DYNAMIC_HEAP_MEMORY
    
/**
 * @class UniquePointer<T,D,A>
 * @brief Unique pointer.
 *
 * @tparam T Data type of an owning object.
 * @tparam D Deleter type for an owning object. 
 * @tparam A Heap memory allocator class.
 */
template <typename T, class D = SmartPointerDeleter<T>, class A = Allocator>
class UniquePointer : public Object<A>, public api::SmartPointer<T>
{
    typedef UniquePointer<T,D,A> Self;
    typedef Object<A> Parent;

public:

    /**
     * @brief Constructor of an empty unique object.
     */
    UniquePointer() 
        : Object<A>()
        , api::SmartPointer<T>()
        , pointer_ (NULLPTR) {
        bool_t const isConstructed( construct() );
        setConstructed(isConstructed);    
    }

    /**
     * @brief Constructor.
     *
     * @note If the unique object is not able to be constructed, an object passed by the pointer will be deleted.
     *
     * @param pointer A pointer to get ownership.
     */
    explicit UniquePointer(T* const pointer) 
        : Object<A>()
        , api::SmartPointer<T>()
        , pointer_ (NULLPTR) {
        bool_t const isConstructed( construct(pointer) );
        setConstructed(isConstructed);    
    }

    /**
     * @brief Destructor.
     */
    virtual ~UniquePointer()
    {
        if( isConstructed() )
        {
            releasePointer();
        }
    }

    #if EOOS_CPP_STANDARD >= 2011

    /**
     * @copydoc eoos::Object::Object(Object&&)
     */       
    UniquePointer(UniquePointer&& obj) noexcept 
        : Object<A>( move(obj) )
        , api::SmartPointer<T>()
        , pointer_ (obj.pointer_) {
    }   

    /**
     * @copydoc eoos::Object::operator=(Object&&)
     */
    UniquePointer& operator=(UniquePointer&& obj) & noexcept
    {
        if( this != &obj && isConstructed() )
        {
            releasePointer();
            pointer_ = obj.pointer_;
            Parent::operator=( move(obj) );            
        }        
        return *this;
    }        

    #endif // EOOS_CPP_STANDARD >= 2011

    /**
     * @copydoc eoos::api::Object::isConstructed()
     */
    virtual bool_t isConstructed() const ///< SCA MISRA-C++:2008 Justified Rule 10-3-1 and Defected Rule 9-3-3
    {
        return Parent::isConstructed();
    }

    /**
     * @brief Casts to boolean data type comparing if the stored pointer does not equal to null.
     *
     * @return Comparation the stored pointer does not equal to null.
     */    
    operator bool_t() const 
    {
        return get() != NULLPTR;
    }

    /**
     * @brief Returns the result of dereferencing the stored pointer.
     *
     * @return The dereferenced stored pointer.
     */
    T& operator*() const
    {
        return *get();
    }

    /**
     * @brief Returns the stored pointer.
     *
     * @return The stored pointer or NULLPTR if no pointer stored.
     */
    T* operator->() const
    {
        return get();
    }

    /**
     * @brief Returns an element of the stored array.
     *
     * @param index An element index.
     * @return An element.
     */    
    T& operator[](uint32_t const index) const
    {
        T* const pointer( get() );
        return pointer[index];
    }

    /**
     * @copydoc eoos::api::SmartPointer::get()
     */
    virtual T* get() const
    {
        T* pointer( NULLPTR );
        if( isConstructed() )
        {
            pointer = pointer_;
        }
        return pointer;
    }

    /**
     * @copydoc eoos::api::SmartPointer::reset()
     */   
    virtual void reset()
    {
        reset(NULLPTR);
    }

    /**
     * @copydoc eoos::api::SmartPointer::reset(T*)
     */
    virtual void reset(T* ptr)
    {
		if (get() != ptr)
        {
            Self temp(ptr);
            swap(temp);
        }
    }

    /**
     * @copydoc eoos::api::SmartPointer::getCount()
     */
    virtual int32_t getCount() const
    {
        int32_t counter( 0 );
        if( isConstructed() && (pointer_ != NULLPTR) )
        {
            counter = 1;
        }
        return counter;
    }
    
    /**
     * @copydoc eoos::api::SmartPointer::isNull()
     */       
    virtual bool_t isNull() const
    {
        return get() == NULLPTR;        
    }
    
    /**
     * @copydoc eoos::api::SmartPointer::isUnique()
     */   
    virtual bool_t isUnique() const
    {
        return getCount() == 1;
    }
    
    /**
     * @brief Swaps this managed object with an object managed by given smart object.
     *
     * @param obj A smart object to swap managed objects.
     */
    void swap(UniquePointer& obj)
    {
        if( isConstructed() )
        {
            if( obj.isConstructed() )
            {
                T* const pointer( pointer_ );
                pointer_ = obj.pointer_;
                obj.pointer_ = pointer;
            }
        }
    }
    
    /**
     * @brief Releases ownership of the managed object.
     *
     * @return Released raw pointer.
     */    
    T* release()
    {
        T* pointer( pointer_ );
        if( isConstructed() )
        {
            pointer_ = NULLPTR;
        }
        return pointer;
    }
    
protected:

    using Parent::setConstructed;     

private:

    /**
     * @brief Constructs this object.
     *
     * @param pointer A pointer to get ownership.
     * @return True if this object has been constructed successfully.
     */     
    bool_t construct(T* const pointer = NULLPTR)
    {
        bool_t res( false );
        do
        {
            if( !isConstructed() )
            {   ///< UT Justified Branch: HW dependency
                D::free(pointer);
                break;
            }
            pointer_ = pointer;
            res = true;
        } while(false);
        return res;
    }
    
    /**
     * @brief Releases the managed object by control block.
     */       
    void releasePointer()
    {
        D::free(pointer_);
        pointer_ = NULLPTR;
    }       
    
    #if EOOS_CPP_STANDARD < 2011
    
    /**
     * @copydoc eoos::Object::Object(Object const&)
     */
    UniquePointer(UniquePointer const&);

    /**
     * @copydoc eoos::Object::operator=(Object const&)
     */       
    UniquePointer& operator=(UniquePointer const&);    

    #endif // EOOS_CPP_STANDARD < 2011

    /**
     * @brief Pointer to the managed object.
     */
    T* pointer_;

};

/**
 * @brief Comparison operator to equal.
 *
 * @param obj1 Reference to object.
 * @param obj2 Reference to object.
 * @return True if objects are equal.
 */
template <typename T, class D, class A>
inline bool_t operator==(UniquePointer<T,D,A> const& obj1, UniquePointer<T,D,A> const& obj2)
{
    return obj1.get() == obj2.get();
}

/**
 * @brief Comparison operator to unequal.
 *
 * @param obj1 Reference to object.
 * @param obj2 Reference to object.
 * @return True if objects are not equal.
 */
template <typename T, class D, class A>
inline bool_t operator!=(UniquePointer<T,D,A> const& obj1, UniquePointer<T,D,A> const& obj2)
{
    return obj1.get() != obj2.get();
}

#endif // EOOS_ENABLE_DYNAMIC_HEAP_MEMORY

} // namespace lib
} // namespace eoos
#endif // LIB_UNIQUEPOINTER_HPP_

Generated by OpenCppCoverage (Version: 0.9.9.0)