//  Beta release only.
//
//  Copyright(c) Ivyware Pty Ltd 2002-9  (all rights reserved)
//               MELBOURNE, VICTORIA, AUSTRALIA, 3130
//
//  Provided no fee is charged (either by the distributer or user)
//  permission to use, copy, modify, and distribute this software and
//  its documentation for any purpose and without fee is hereby granted,
//  provided that the above copyright notice appear in all copies and
//  that both that copyright notice and this permission notice appear
//  in supporting documentation.
//
//  No waranty or suitablity for purpose is implied.
//
//
//  P2Peer template library
//
#pragma once
#include "P2PmsgKERNEL.h"

//
//  P2Peer library Safe Pointer template
//  NOTES: Primarily used to recover memory from stack based pointers
//         when exceptions are thrown
//       : Designed for use within the P2Peer environment and not
//         to conflict with alternatives
//
template<class SafePtrType>
class P2PSafePtr
{
    // Constructors and destruction
    public:
        P2PSafePtr ()
        {
          m_pSafePtrType =  0;
          m_pRefCount    = &m_nRefCount;
          m_nRefCount    =  0;
        }

        P2PSafePtr ( SafePtrType *pSafePtrType )
        {
          if ( pSafePtrType )
          {
            m_pSafePtrType =  pSafePtrType;
            m_pRefCount    = &m_nRefCount;;
            m_nRefCount    =  1;
          }
          else
          {
            m_pSafePtrType =  0;
            m_pRefCount    = &m_nRefCount;
            m_nRefCount    =  0;
          }
        }

        P2PSafePtr ( const P2PSafePtr& rSafePtr )
        {
          m_pSafePtrType = rSafePtr.m_pSafePtrType;
          if ( rSafePtr.m_pRefCount == &rSafePtr.m_pRefCount )
            rSafePtr.SwapRef2Shared ( );
          m_pRefCount    = rSafePtr.m_pRefCount;
        (*m_pRefCount)++;
        }

       ~P2PSafePtr ( ) { Delete(); }

    // Overloaded operators
    public:
      SafePtrType*
        operator->() const { return  m_pSafePtrType; }
      SafePtrType&
        operator*()  { return *m_pSafePtrType; }
      
        operator SafePtrType*() { return m_pSafePtrType; }

        operator bool () { return m_pSafePtrType ? true : false; }

      P2PSafePtr&
        operator = ( P2PSafePtr& rSafePtr )
        {
          if ( this == &rSafePtr )
            return *this;
          Delete();
          rSafePtr.SwapRef2Shared ( );
          m_pSafePtrType = rSafePtr.m_pSafePtrType;
          m_pRefCount    = rSafePtr.m_pRefCount;
          if ( m_pSafePtrType )
            (*m_pRefCount)++;
          return *this;
        }

      P2PSafePtr&
        operator = ( SafePtrType *pSafePtrType )
        {
          if ( m_pSafePtrType == pSafePtrType )
            return *this;
          Delete();
          if ( pSafePtrType == NULL )
            return *this;
          m_pSafePtrType =  pSafePtrType;
          m_pRefCount    = &m_nRefCount;
         *m_pRefCount    =  1;
          return *this;
        }

    // Operations
    public:
      SafePtrType*
        Dereference ()
        {
          SafePtrType *pSafePtrType = m_pSafePtrType;
          if ( m_pRefCount != &m_nRefCount )
            delete m_pRefCount;
                   m_pRefCount = NULL;
          m_pSafePtrType = 0;
          return pSafePtrType;
        }
      bool
        IsEmpty ()
        {
          return m_pSafePtrType == NULL ? true : false;
        }

    // Utilities
    private:
      void
        Delete ()
        {
          if (  m_pRefCount == NULL ||
               *m_pRefCount == NULL    )
          {
            ASSERT(m_pSafePtrType == NULL);
            return;
          }
          ASSERT(m_pSafePtrType != NULL);
          if ( --(*m_pRefCount) == 0 )
          {
            if ( m_pRefCount != &m_nRefCount )
              delete m_pRefCount;
                     m_pRefCount = NULL;
            delete m_pSafePtrType;
                   m_pSafePtrType  = NULL;
          }
        }
      void
        SwapRef2Shared ( )
        {
          if ( m_pRefCount == &m_nRefCount )
          {
            m_pRefCount = new int;
           *m_pRefCount = m_nRefCount;
            m_nRefCount = 0;
          }
        }

    // Attributes
    private:
        SafePtrType *m_pSafePtrType;
        int         *m_pRefCount;
        int          m_nRefCount;
};

//
//  Stack based P2Peer library Safe Lock template
//  NOTES: Primarily for use with objects supporting Lock() and
//         Unlock() methods.  Usually resides on the stack.
//
template<class SafeLockType>
class P2PSafeLock
{
    // Constructors and destruction
    public:
        P2PSafeLock ()
        {
          m_pSafeLockType =  0;
        }

        P2PSafeLock ( SafeLockType& oSafeLockType )
        {
          m_pSafeLockType =  &oSafeLockType;
          if ( m_pSafeLockType )
            m_pSafeLockType -> Lock ( );
        }

       ~P2PSafeLock ( ) { Unlock(); }

    // Overloaded operators
    public:
      P2PSafeLock&
        operator = ( SafeLockType *pSafeLockType )
        {
          if ( m_pSafeLockType == pSafeLockType )
            return *this;
          Unlock();
          if ( pSafeLockType == NULL )
            return *this;
          m_pSafeLockType =  pSafeLockType;
          return *this;
        }

    // Operations
    public:
      bool
        IsEmpty ()
        {
          return m_pSafeLockType == NULL ? true : false;
        }

    // Utilities
    private:
      void
        Unlock ()
        {
          if ( m_pSafeLockType )
            m_pSafeLockType -> Unlock();
          m_pSafeLockType = 0;
        }

    // Attributes
    private:
        SafeLockType *m_pSafeLockType;
};
