/* $Id: pointer.h,v 2.6 1999/11/01 21:31:50 v2kea318 Exp $

$Log: pointer.h,v $
 * Revision 2.6  1999/11/01  21:31:50  v2kea318
 * *** empty log message ***
 *
 * Revision 2.5  1999/10/25  17:37:03  v2kea318
 * Eliminate the osi_thread.h inclusion and move the relevant mutex
 * functions to private methods in the InternalContainerThreadSafe class.
 *
 * Revision 2.4  1999/10/22  20:09:31  v2kea318
 * Rename thread.h osi_thread.h.
 *
 * Revision 2.3  1999/10/14  16:26:19  v2kea318
 * Implement the mutex stuff using routines from thread.h, which provides
 * a pseudo machine independant mutex interface.
 *
 * Revision 2.2  1999/09/21  19:25:33  v2kea318
 * Only throw a NullPointerException if the macro POINTER_THROW_ON_NULL
 * is defined by the user.
 *
 * Revision 2.1  1999/09/09  16:24:26  v2kea318
 * Major redesign of the internals of the class.  The raw pointer is now
 * stored in an internal container class which implements much of the
 * old behavior of the Pointer class itself.  A thread-safe container
 * class is derived from this container, which stores a mutex (POSIX)
 * and wraps write access to the reference counter pointer with a lock
 * on this mutex, where appropriate.  The Pointer class constructor now
 * has an optional bool parameter which specifies if the thread-safe version
 * of the container is to be used.  The default is false to maintain the
 * old class behavior.
 *
 * Revision 1.20  1999/07/30  20:52:39  v2kea318
 * Added the release() method.
 *
 * Revision 1.19  1999/05/13  18:40:02  v2kea318
 * Added a NullPointerException, which is thrown whenever we try to dereference
 * a NULL pointer.  The checking is done in the get() method.  The old get()
 * method, which simply returned the raw pointer, is now called raw().
 *
 * Revision 1.18  1999/05/06  19:51:46  v2kea318
 * *** empty log message ***
 *
 * Revision 1.17  1999/04/23  18:50:33  v2kea318
 * *** empty log message ***
 *
 * Revision 1.16  1999/04/23  18:48:17  v2kea318
 * *** empty log message ***
 *
 * Revision 1.15  1998/06/02  09:27:11  saaron
 * Add some internal functions to manage the counter and the memory management.
 *
Revision 1.14  1997/05/16 11:29:19  saaron
Got rid of the bool.h inclusion.

Revision 1.13  1997/05/09 09:35:54  saaron
*** empty log message ***

Revision 1.12  1997/01/23 15:46:25  saaron
Changed the == operators to have two template types.  This
eliminates the need for the (Pointer<T>, void*) version.

Revision 1.11  1997/01/10 09:40:39  saaron
Rewrote the pointer operators to return const values for the const
version and non const values for the non const versions.

Revision 1.10  1997/01/06 18:33:56  saaron
Fixed the operator bool() to be const.

*****************************************************************************

Define a true smart pointer container.  The container will keep track of
all the references to a dynamically allocated object.  When no more
references are found, the memory will be deleted.  Unlike auto_ptr,
this object can be stored in STL containers.

*****************************************************************************/

#ifndef POINTER_MYLIBS_H
#define POINTER_MYLIBS_H

#include <iostream>   // Define NULL
#include <cstddef>    // Define size_t
#include <stdexcept>

#ifdef _WIN32
#include <windows.h>
typedef CRITICAL_SECTION pointer_mutex_t;
#else
#include "Pthread.h"
typedef pthread_mutex_t pointer_mutex_t;
#endif

//***************************************************************************

class NullPointerException : public runtime_error {
public:
  NullPointerException() : runtime_error("dereferencing NULL pointer") {}
};

//***************************************************************************

template <class T>
class Pointer {
private:
  typedef size_t CounterType;
  typedef T value_type;

  // define the internal data container.
  class InternalContainer {
  private:
    T* ptr;
    CounterType* refcnt;
    
    /* Make assignment operator and copy constructor private.  These 
       should never be used by the Pointer class, but we'll make sure. */
    InternalContainer(const InternalContainer& i);
    InternalContainer& operator = (const InternalContainer& i);
  public:
    InternalContainer(T* p = NULL) 
      : ptr(p), refcnt( (p) ? new CounterType(1) : NULL ) {}
    virtual ~InternalContainer() { release(); };

    // release the memory.
    void release() {
      decr_count();
      cleanup();
    }

    // pointer operators.
    const T* raw() const { return ptr; }
          T* raw()       { return ptr; }
    const T* get() const {
#ifdef POINTER_THROW_ON_NULL
      if (!ptr) throw NullPointerException();
#endif
      return raw();
    }
    T* get() {
#ifdef POINTER_THROW_ON_NULL
      if (!ptr) throw NullPointerException();
#endif
      return raw();
    }

    // how many references?
    CounterType count() const { return (refcnt) ? *refcnt : 0; }

    // manipulate the counter and memory management.
    virtual void cleanup()
      {
	if (count() == 0) {
	  delete ptr; ptr = NULL;
	  delete refcnt; refcnt = NULL;
	}
      }
    virtual void incr_count()
      {
	if (refcnt) ++(*refcnt);
      }
    virtual void decr_count()
      {
	if (refcnt) --(*refcnt);
      }
  };

  // derive a special thread-safe container.
  class InternalContainerThreadSafe : public InternalContainer {
  private:
    pointer_mutex_t* mutex;
    // define some methods for the raw mutex.
    void lock_mutex() { 
#ifdef _WIN32
      EnterCriticalSection(mutex);
#else
      pthread_mutex_lock(mutex);
#endif
    }
    void unlock_mutex() {
#ifdef _WIN32
      LeaveCriticalSection(mutex);
#else
      pthread_mutex_unlock(mutex);
#endif
    }
    void new_mutex() {
      mutex = new pointer_mutex_t;
#ifdef _WIN32
      InitializeCriticalSection(mutex);
#else
      pthread_mutex_init(mutex, NULL);
#endif
    }
    void destroy_mutex() {
#ifdef _WIN32
      DeleteCriticalSection(mutex);
#else
      pthread_mutex_destroy(mutex);
#endif
      delete mutex;
      mutex = NULL;
    }
    void lock() { lock_mutex(); }
    void unlock() { unlock_mutex(); }
    InternalContainerThreadSafe(const InternalContainerThreadSafe&);
    InternalContainerThreadSafe& operator = 
      (const InternalContainerThreadSafe&);
  public:
    InternalContainerThreadSafe(T* p = NULL) 
      : InternalContainer(p)
      {
	new_mutex();
      }

    ~InternalContainerThreadSafe() 
      {
	if (count() == 0) {
	  destroy_mutex();
	}
      }
      
    // memory management and counter stuff.
    void cleanup() {
      lock();
      InternalContainer::cleanup();
      unlock();
    }
    void incr_count() {
      lock(); InternalContainer::incr_count(); unlock();
    }
    void decr_count() {
      lock(); InternalContainer::decr_count(); unlock();
    }
  };
      
public:
  // Constructors.
  explicit Pointer(T* p = NULL, bool tsafe = false)
    : icon((tsafe) ? new InternalContainerThreadSafe(p)
	           : new InternalContainer(p))
    {}
  Pointer(const Pointer<T>& a) : icon(a.icon) { icon->incr_count(); }
  
  // Destructor.
  ~Pointer() { release(); }

  // release the memory.
  void release() 
    { 
      if (icon) {
	icon->release(); 
	if (raw() == NULL) {  /* release has destroyed the pointer so
				 we can destroy the container. */
	  delete icon; icon = NULL; 
	}
      }
    }
  
  // Assignment operator
  Pointer<T>& operator = (const Pointer<T>& a)
    {
      if (this != &a) {
	if (icon) icon->cleanup();
	icon = a.icon;
	icon->incr_count();
      }
      return *this;
    }
  
  // Pointer operators.
  const T* raw() const { return icon->raw(); };
        T* raw()       { return icon->raw(); };
  const T* get() const { return icon->get(); }
        T* get()       { return icon->get(); }
  const T& operator *  () const { return *get(); };
        T& operator *  ()       { return *get(); };
  const T* operator -> () const { return  get(); };
        T* operator -> ()       { return  get(); };
    
  // Provide a bool operator so that constructions like if (a) can be used.
  operator bool() const { return (raw() != NULL); };
  
  // How many references?
  CounterType count() const { return icon->count(); };
  
private:
  InternalContainer* icon;
};

// Other operators.
template <class T1>
inline bool operator == (const Pointer<T1>& p1, const void* p2)
{
    return p1.raw() == p2;
}

template <class T1, class T2>
inline bool operator == (const Pointer<T1>& p1, const Pointer<T2>& p2)
{
    return p1.raw() == p2.raw();
}

#endif
