// ==========================================================================
// $Id: smart_pointer.hh,v 1.3 2006/11/20 04:52:11 jlang Exp $
// CSI2372 example Code for lecture 13
// ==========================================================================
// (C)opyright:
//
//   Jochen Lang
//   SITE, University of Ottawa
//   800 King Edward Ave.
//   Ottawa, On., K1N 6N5
//   Canada. 
//   http://www.site.uottawa.ca
// 
// Creator: jlang (Jochen Lang)
// Email:   jlang@site.uottawa.ca
// ==========================================================================
// $Log: smart_pointer.hh,v $
// Revision 1.3  2006/11/20 04:52:11  jlang
// Check-in for lecture 13
//
// Revision 1.2  2006/11/19 22:53:31  jlang
// Smart pointer example with ref-count
//
// Revision 1.1  2006/11/19 18:30:50  jlang
// Added smart pointer -- not compiled
//
//
// ==========================================================================
#ifndef SMART_PTR_HH_
#define SMART_PTR_HH_

#include <iostream>
#include <stdexcept>

/**
 * Smart pointer class implementing refrence counting
 * partially based on  A. Alexandrescu, Modern C++ Design, Chpt. 7
 */
template <class T>
class SmartPtr {
  /** Helper class for reference counter */
  class RefCounter {
    unsigned int d_pCount;
  public:
    RefCounter() : d_pCount(1){};
    void clone() {
      ++d_pCount;
      return;
    }
    bool release() {
      if (!--d_pCount) {
	return true;
      } 
      return false;
    }
  };

  class Tester {
    void operator delete(void *);
  };

  // The object pointed to
  T* d_pointee;
  RefCounter* d_counter;
  
public:
  // constructor from native pointer - do not allow null
  explicit SmartPtr( T* _pointee ) 
    : d_pointee(_pointee), d_counter(new RefCounter()) {
    if (d_pointee == 0) {
      delete d_counter;
      throw std::runtime_error("Smart pointer cannot be  null"); 
    }
  }
  // copy constructor from other smart pointer 
  SmartPtr(SmartPtr& _src) 
  // share the object and the reference counter
    : d_pointee(_src.d_pointee), d_counter(_src.d_counter) {
    d_counter->clone(); // increase the counter
  }
  // delete this smart pointer
  ~SmartPtr() {
    // decrease ref count and check if we are the last pointer to object
    if ( d_counter->release() ) {
      delete d_pointee; // delete object
      delete d_counter; // delete counter object
    }
  }
  // assign an smart ptr to this smart ptr
  SmartPtr<T>& operator=(const SmartPtr<T>& _src) {
    if (this != &_src) {
      // decrease ref count and check if we are the last pointer to object
      if ( d_counter->release() ) {
	delete d_pointee; // delete object
	delete d_counter; // delete counter object
      }
      // assign the object and ref. counter from _src
      d_counter = _src.d_counter;
      d_counter->clone();
      d_pointee = _src.d_pointee;
    }
    return *this;
  }
  // get the object
  T& operator*() {
    return *d_pointee;
  }
  // get the pointer to be used in -> operator
  T* operator->() {
    return d_pointee;
  }
  
  /*-------------------------------------------------
   * Member functions which expose d_pointee
   *------------------------------------------------*/
  // Get at the object pointer
  T* GetImpl() {
    return d_pointee;
  }
  // Get at the address of the object pointer
  T*& GetImplRef() {
    return &d_pointee;
  }
  // Reset this smart pointer to point to source
  void reset(T* _src) {
    // decrease ref count and check if we are the last pointer to object
    if ( d_counter->release() ) {
      delete d_pointee; // delete object
      delete d_counter; // delete counter object
    }
    d_pointee = _src;
    d_counter = new RefCounter();
    if (d_pointee == 0) {
      delete d_counter;
      throw std::runtime_error("Smart pointer cannot be null"); 
    }
  }

  // Release this smart pointer and set dest to pointee
  // - not consistent with non-zero pointer policy
// void release(T*& _dest) {
//     _dest = d_pointee;
//     d_pointee = 0;
//     if ( d_counter->release() ) {
//       delete d_counter; // delete counter object
//     }
//   }

  /*-------------------------------------------------
   * Operators for comparison
   *------------------------------------------------*/
  // if (!ptr) -- redundant here since d_pointee is never 0
  bool operator!() const {
    return d_pointee == 0;
  }
  // if ( sPtrA == ptrB ) ...
  inline bool operator==(const T *rhs ) {
    return d_pointee == rhs;
  }
  // if ( ptrA == sPtrB ) ...
  inline friend bool operator==( const T *lhs, 
				 const SmartPtr<T>& rhs) {
    return lhs == rhs.d_pointee;
  }
  // if ( sPtrA != ptrB ) ...
  inline bool operator!=(const T *rhs ) {
    return d_pointee != rhs;
  }
  // if ( ptrA != sPtrB ) ...
  inline friend bool operator!=( const T *lhs, 
				 const SmartPtr<T>& rhs) {
    return lhs != rhs.d_pointee;
  }
  // Enable equal comparison in class hierarchies 
  template <class U> 
  bool operator==( const U* rhs ) {
    return d_pointee == rhs;
  }
  template <class U> 
  inline friend bool operator==( const U* rhs,
				 const SmartPtr<T>& lhs ) {
    return rhs == lhs.d_pointee;
  }
  // Enable not equal comparison in class hierarchies
  template <class U> 
  bool operator!=( const U* rhs ) {
    return d_pointee != rhs;
  }
  template <class U> 
  inline friend bool operator!=( const U* rhs,
				 const SmartPtr& lhs ) {
    return rhs != lhs.d_pointee;
  }
  // comparison with another smart pointer
  template <class U> 
  bool operator==( const SmartPtr<U>& rhs ) const {
    return d_pointee == rhs.d_pointee;
  }
  template <class U> 
  bool operator!=( const SmartPtr<U>& rhs ) const {
    return d_pointee != rhs.d_pointee;
  }
  // conversion operator to Tester 
  // this will enable if (smartPtr) 
  //  -- redundant here since d_pointee is never 0
  operator Tester*() const {
    if (!d_pointee ) return 0;
    static Tester test;
    return &test;
  }
};

#endif
