#ifndef HANDLE_H
#define HANDLE_H

///_____________________________________________________________________________
///
/// \brief
/// Handle class template - a simplified version of boost::intrusive_ptr. 
///
/// It's a smart pointer for objects with "built-in" reference count. 
/// Such implemetation makes the smallest possible smart pointer - its 
/// size is equivalent to just a single raw pointer.
///
///_____________________________________________________________________________

#include <iostream>

template <typename T>
class Handle {

public:
    ///
    /// default ctor
    ///
    Handle();
    
    ///
    /// ctor from a raw pointer
    ///
    Handle(T* ptr);
    
    ///
    /// copy ctor
    ///        
    Handle(const Handle& rhs);
    
    ///
    /// dtor
    ///
    ~Handle();

    ///
    /// assignment operator
    ///
    Handle& operator=(const Handle& rhs);
    
    ///
    /// operator-> allows usage with pointer semantics
    /// 
    T* operator->() const;
    
    ///
    /// dereference operator
    ///
    T& operator*() const;

    ///
    /// release - set pointer to 0
    ///
    void release();

    ///
    /// == operator - compares raw pointers
    ///
    bool operator==(const Handle& rhs);
    
    ///
    /// != operator - compares raw pointers
    ///
    bool operator!=(const Handle& rhs);

private:
    T* p;
    
    void init();
};


template <typename T>
Handle<T>::Handle() : p(0)
{}

///
/// ctor from a raw pointer
///
template <typename T>
Handle<T>::Handle(T* realPtr) : p(realPtr)
{
    init();
}

///
/// copy ctor
///        
template <typename T>
Handle<T>::Handle(const Handle& rhs) : p(rhs.p)
{
    init();
}


///
/// assignment operator
///
template <typename T>
Handle<T>& Handle<T>::operator=(const Handle& rhs)
{
    if (p != rhs.p) {
        if (p) {
            p->release();
        }

        p = rhs.p;
        init();
    }
    return *this;
}


///
/// init
///
template <typename T>
void Handle<T>::init()
{
    if (p == 0) {
        return;
    }

    p->add_ref();
}

///
/// dtor
///
template <typename T>
Handle<T>::~Handle()
{
    if (p) p->release();
}


///
/// operator-> allows usage with pointer semantics
/// 
template <typename T>
T* Handle<T>::operator->() const 
{ 
    return p; 
}


///
/// dereference operator
///
template <typename T>
T& Handle<T>::operator*() const 
{ 
    return *p; 
}

///
/// release - set pointer to 0
///
template <typename T>
void Handle<T>::release()
{
    if (p) p->release();
    p = 0;
}
 

///
///  == operator - compares raw pointers
///
template <typename T>
bool Handle<T>::operator==(const Handle& rhs)
{
    if (p == rhs.p)
       return true;

    return false;
}

///
///  != operator - compares raw pointers
///
template <typename T>
bool Handle<T>::operator!=(const Handle& rhs)
{
    if (p != rhs.p)
       return true;

    return false;
}
   
///
/// Handle<T> can be printed with "cout << handle;"
///
template <typename T>
std::ostream& operator<<(std::ostream& o, const Handle<T>& handle)
{
    o << *handle;
    return o;
}

#endif  // HANDLE_H
