# pragma once

template <typename T>
class com_ptr
{
//private:
//    typedef void (com_ptr::*bool_type)() const;
//    void safe_bool() const {}
//
//
public:
    com_ptr():
        _ptr(nullptr)
    {
    }

    com_ptr(T* ptr, bool add_ref = false):
        _ptr(ptr)
    {
        if (_ptr && add_ref)
            _ptr->AddRef();
    }

    com_ptr(const com_ptr& rhs):
        _ptr(rhs._ptr)
    {
        if (_ptr)
            _ptr->AddRef();
    }

    com_ptr(com_ptr&& rhs):
        _ptr(rhs._ptr)
    {
        rhs._ptr = nullptr;
    }

    template <typename U>
    com_ptr(const com_ptr<U>& rhs):
        _ptr(rhs._ptr)
    {
        if (_ptr)
            _ptr->AddRef();
    }

    ~com_ptr()
    {
        if (_ptr)
            _ptr->Release();
    }

    com_ptr& operator = (const com_ptr& rhs)
    {
        com_ptr(rhs).swap(*this);
        return *this;
    }

    com_ptr& operator = (com_ptr&& rhs)
    {
        com_ptr(static_cast<com_ptr&&>(rhs)).swap(*this);
        return *this;
    }

    template <typename U>
    com_ptr& operator = (const com_ptr<U>& rhs)
    {
        com_ptr(rhs).swap(*this):
        return *this;
    }

    com_ptr& operator = (T* rhs)
    {
        com_ptr(rhs).swap(*this);
        return *this;
    }

    //operator bool_type() const
    //{
    //    return _ptr ? &com_ptr::safe_bool : nullptr;
    //}

    //bool_type operator !() const
    //{
    //    return !((bool_type)*this);
    //}

    void reset()
    {
        com_ptr().swap(*this);
    }

    void reset(T* rhs)
    {
        com_ptr(rhs).swap(*this);
    }

    void reset(T* rhs, bool add_ref)
    {
        com_ptr(rhs, add_ref).swap(*this);
    }

    T* get() const
    {
        return _ptr;
    }

    T* detach()
    {
        auto result = _ptr;
        _ptr = nullptr;
        return result;
    }

    void swap(com_ptr& rhs)
    {
        T* temp = rhs._ptr;
        rhs._ptr = _ptr;
        _ptr = temp;
    }

    T&  operator *  () const { return *_ptr; }
    T*  operator -> () const { return  _ptr; }

        operator T* () const { return  _ptr; }
    T** operator &  ()       { return &_ptr; }

private:
    T*      _ptr;
};

template <typename T, typename U> inline bool operator==(const com_ptr<T>& a, const com_ptr<U>& b) { return a.get() == b.get(); }
template <typename T, typename U> inline bool operator!=(const com_ptr<T>& a, const com_ptr<U>& b) { return a.get() != b.get(); }
template <typename T, typename U> inline bool operator==(const com_ptr<T>& a, U* b) { return a.get() == b; }
template <typename T, typename U> inline bool operator!=(const com_ptr<T>& a, U* b) { return a.get() != b; }
template <typename T, typename U> inline bool operator==(T* a, const com_ptr<U>& b) { return a == b.get(); }
template <typename T, typename U> inline bool operator!=(T* a, const com_ptr<U>& b) { return a != b.get(); }
template <typename T> inline bool operator==(const com_ptr<T>& p, std::nullptr_t) { return p.get() == nullptr; }
template <typename T> inline bool operator==(std::nullptr_t, const com_ptr<T>& p) { return p.get() == nullptr; }
template <typename T> inline bool operator!=(const com_ptr<T>& p, std::nullptr_t) { return p.get() != nullptr; }
template <typename T> inline bool operator!=(std::nullptr_t, const com_ptr<T>& p) { return p.get() != nullptr; }
template <typename T> inline bool operator<(const com_ptr<T>& a, const com_ptr<T>& b) { return std::less<T*>()(a.get(), b.get()); }
template <typename T> inline bool operator<=(const com_ptr<T>& a, const com_ptr<T>& b) { return std::less_equal<T*>()(a.get(), b.get()); }
template <typename T> inline bool operator>(const com_ptr<T>& a, const com_ptr<T>& b) { return std::greater<T*>()(a.get(), b.get()); }
template <typename T> inline bool operator>=(const com_ptr<T>& a, const com_ptr<T>& b) { return std::greater_equal<T*>()(a.get(), b.get()); }
template <typename T> void swap(com_ptr<T> & lhs, com_ptr<T> & rhs) { lhs.swap(rhs); }