// Description: Read protection
#ifndef PASTELSYS_READ_PROTECTED_H
#define PASTELSYS_READ_PROTECTED_H
#include "pastel/sys/mytypes.h"
#include "pastel/sys/ensure.h"
#include <utility>
#include <memory>
namespace Pastel
{
    template <typename Type>
    class ReadProtected;
    template <typename Type>
    class ReadPtr
    {
    public:
        ReadPtr()
            : read_()
        {
        }
        ReadPtr(const ReadPtr& that)
            : read_(that.read_)
        {
        }
        ReadPtr(ReadPtr&& that)
            : read_(std::move(that.read_))
        {
        }
        template <typename That>
        bool operator==(const ReadPtr<That>& that) const
        {
            return get() == that.get();
        }
        template <typename That>
        bool operator!=(const ReadPtr<That>& that) const
        {
            return !(*this == that);
        }
        void swap(ReadPtr& that)
        {
            read_.swap(that.read_);
        }
        void clear()
        {
            read_.reset();
        }
        integer readCount() const
        {
            return read_.use_count();
        }
        const Type* get() const
        {
            return *read_;
        }
        const Type* operator->() const
        {
            return *read_;
        }
        const Type& operator*() const
        {
            return **read_;
        }
    private:
        friend class ReadProtected<Type>;
        explicit ReadPtr(const Type* that)
            : read_(new const Type*)
        {
            *read_ = that;
        }
        void swapPtr(ReadPtr& that)
        {
            ASSERT(read_ && that.read_);
            std::swap(*read_, *that.read_);
        }
        std::shared_ptr<const Type*> read_;
    };
    template <typename Type>
    class ReadProtected
    {
    public:
        explicit ReadProtected(const Type* that)
            : read_(that)
        {
        }
        ~ReadProtected()
        {
            readProtect();
        }
        void readProtect()
        {
            ENSURE_OP(readCount(), ==, 1);
        }
        integer readCount() const
        {
            return read_.readCount();
        }
        void swap(ReadProtected& that)
        {
            read_.swap(that.read_);
            read_.swapPtr(that.read_);
        }
        ReadPtr<Type> read() const
        {
            return read_;
        }
    private:
        ReadProtected();
        ReadProtected(ReadProtected&& that);
        ReadProtected(const ReadProtected& that);
        ReadProtected& operator=(ReadProtected that);
        ReadPtr<Type> read_;
    };
}
#endif