/**
 * @file      lib.SharedPointer.hpp
 * @author    Sergey Baigudin, sergey@baigudin.software
 * @copyright 2020-2022, Sergey Baigudin, Baigudin Software
 */
#ifndef LIB_SHAREDPOINTER_HPP_
#define LIB_SHAREDPOINTER_HPP_

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

namespace eoos
{
namespace lib
{

#ifdef EOOS_ENABLE_DYNAMIC_HEAP_MEMORY
    
/**
 * @class SharedPointer<T,D,A>
 * @brief Shared 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 SharedPointer : public Object<A>, public api::SmartPointer<T>
{
    typedef SharedPointer<T,D,A> Self;
    typedef Object<A> Parent;

public:

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

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

    /**
     * @brief Destructor.
     */
    virtual ~SharedPointer()
    {
        if( isConstructed() )
        {
            release();
        }
    }

    /**
     * @copydoc eoos::Object::Object(Object const&)
     */
    SharedPointer(SharedPointer const& obj) ///< SCA MISRA-C++:2008 Justified Rule 12-8-1
        : Object<A>(obj)
        , api::SmartPointer<T>()
        , cb_(obj.cb_){
        acquire();
    }

    /**
     * @copydoc eoos::Object::operator=(Object const&)
     */       
    SharedPointer& operator=(SharedPointer const& obj)
    {
        if( isConstructed() && (this != &obj) )
        {
            release();
            cb_ = obj.cb_;
            acquire();            
            Parent::operator=(obj);            
        }
        return *this;
    }    

    #if EOOS_CPP_STANDARD >= 2011

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

    /**
     * @copydoc eoos::Object::operator=(Object&&)
     */
    SharedPointer& operator=(SharedPointer&& obj) & noexcept
    {
        if( isConstructed() && (this != &obj) )
        {
            release();            
            cb_ = obj.cb_;
            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 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 = cb_->getPointer();
        }
        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() )
        {
            if( cb_->getPointer() != NULLPTR )
            {
                counter = cb_->getCounter();
            }
        }
        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(SharedPointer& obj)
    {
        if( isConstructed() && obj.isConstructed() )
        {
            ControlBlock<T,D,A>* const cb( cb_ );
            cb_ = obj.cb_;
            obj.cb_ = cb;
        }
    }    
    
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;
            }
            cb_ = new ControlBlock<T,D,A>(pointer);
            if(cb_ == NULLPTR)
            {
                D::free(pointer);
                break;
            }
            if( !cb_->isConstructed() )
            {   ///< UT Justified Branch: HW dependency
                D::free(pointer);                
                delete cb_;
                cb_ = NULLPTR;
                break;
            }
            res = true;
        } while(false);
        return res;
    }

    /**
     * @brief Releases the managed object by control block.
     */       
    void release()
    {
        if( cb_ != NULLPTR )
        {
            int32_t const counter( cb_->decrease() );
            if(counter == 0)
            {
                D::free(cb_->getPointer());
                delete cb_;
                cb_ = NULLPTR;            
            }
        }
    }        

    /**
     * @brief Acquires a managed object by control block.
     */
    void acquire()
    {
        if( cb_ != NULLPTR )
        {
            cb_->increase();
        }
    }

    /**
     * @class ControlBlock<TT,DD,AA>
     * @brief Primary template implementation of shared pointer control block class.
     *
     * @tparam TT Data type of owning the object.
     * @tparam DD Deleter type for owning the object.  
     * @tparam AA Heap memory allocator class.
     *
     * @note This class is implemented as an auxiliry class for SharedPointer
     * and is tested for construction before usage. Therefore, some checks
     * are skipped in public interface to speedup performence. 
     */
    template <typename TT, class DD, class AA> 
    class ControlBlock : public NonCopyable<AA>
    {
        typedef NonCopyable<AA> Parent;    

    public:

        /**
         * @brief Constructor.
         *
         * @param pointer A pointer to get ownership.
         */
        explicit ControlBlock(T* const pointer) 
            : NonCopyable<AA>()
            , pointer_(pointer)
            , counter_(1)
            , mutex_() {
            bool_t const isConstructed( construct() );
            setConstructed(isConstructed);
        }

        /**
         * @copydoc eoos::api::Object::isConstructed()
         */
        virtual bool_t isConstructed() const
        {
            return Parent::isConstructed();
        }

        /**
         * @brief Destructor.
         */
        virtual ~ControlBlock() {}    

        /**
         * @brief Increases the counter on one.
         */
        void increase()
        {
            MutexGuard<AA> const guard(mutex_);
            static_cast<void>(guard);            
            ++counter_;
        }

        /**
         * @brief Decreases the counter on one.
         *
         * @return A value of the counter after decreasing.
         */        
        int32_t decrease()
        {
            MutexGuard<AA> const guard(mutex_);
            static_cast<void>(guard);            
            return --counter_;
        }

        /**
         * @brief Returns the counter.
         *
         * @return A value of the counter.
         */        
        int32_t getCounter() const
        {    
            return counter_;
        }

        /**
         * @brief Returns the managed raw pointer.
         *
         * @return The managed raw pointer.
         */        
        TT* getPointer() const
        {
            return pointer_;
        }    

    private:
    
        /**
         * @brief Constructs this object.
         */     
        bool_t construct()
        {
            bool_t res( false );
            do
            {
                if( !isConstructed() )
                {   ///< UT Justified Branch: HW dependency
                    break;
                }
                if( !mutex_.isConstructed() )
                {   ///< UT Justified Branch: HW dependency
                    break;
                }
                res = true;
            } while(false);
            return res;
        }
        
        /**
         * @brief An owned pointer.
         */
        TT* pointer_;
        
        /**
         * @brief Counter of copies of the shared objects.
         */
        int32_t counter_;
    
        /**
         * @brief Mutex to protect the counter.
         */    
        Mutex<AA> mutex_;
    };

    /**
     * @brief Control block of the managed object.
     */
    ControlBlock<T,D,A>* cb_;

};

/**
 * @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 = SmartPointerDeleter<T>, class A = Allocator>
inline bool_t operator==(SharedPointer<T,D,A> const& obj1, SharedPointer<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 = SmartPointerDeleter<T>, class A = Allocator>
inline bool_t operator!=(SharedPointer<T,D,A> const& obj1, SharedPointer<T,D,A> const& obj2)
{
    return obj1.get() != obj2.get();
}

#endif // EOOS_ENABLE_DYNAMIC_HEAP_MEMORY

} // namespace lib
} // namespace eoos
#endif // LIB_SHAREDPOINTER_HPP_

Generated by OpenCppCoverage (Version: 0.9.9.0)