/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Scott Collins <scc@mozilla.org> (original author of nsCOMPtr)
 *   L. David Baron <dbaron@dbaron.org>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#ifndef nsAutoPtr_h___
#define nsAutoPtr_h___

  // Wrapping includes can speed up compiles (see "Large Scale C++ Software Design")
#ifndef nsCOMPtr_h___
  // For |already_AddRefed|, |nsDerivedSafe|, |NSCAP_Zero|,
  // |NSCAP_DONT_PROVIDE_NONCONST_OPEQ|,
  // |NSCAP_FEATURE_INLINE_STARTASSIGNMENT|
#include "nsCOMPtr.h"
#endif

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

// template <class T> class nsAutoPtrGetterTransfers;

template <class T>
class nsAutoPtr
  {
    private:
      void**
      begin_assignment()
        {
          assign(0);
          return NS_REINTERPRET_CAST(void**, &mRawPtr);
        }

      void
      assign( T* newPtr )
        {
          T* oldPtr = mRawPtr;
          mRawPtr = newPtr;
          delete oldPtr;
        }

      // |class Ptr| helps us prevent implicit "copy construction"
      // through |operator T*() const| from a |const nsAutoPtr<T>|
      // because two implicit conversions in a row aren't allowed.
      // It still allows assignment from T* through implicit conversion
      // from |T*| to |nsAutoPtr<T>::Ptr|
      class Ptr
        {
          public:
            Ptr( T* aPtr )
                  : mPtr(aPtr)
              {
              }

            operator T*() const
              {
                return mPtr;
              }

          private:
            T* mPtr;
        };

    private:
      T* mRawPtr;

    public:
      typedef T element_type;
      
     ~nsAutoPtr()
        {
          delete mRawPtr;
        }

        // Constructors

      nsAutoPtr()
            : mRawPtr(0)
          // default constructor
        {
        }

      nsAutoPtr( Ptr aRawPtr )
            : mRawPtr(aRawPtr)
          // construct from a raw pointer (of the right type)
        {
        }

      nsAutoPtr( nsAutoPtr<T>& aSmartPtr )
            : mRawPtr( aSmartPtr.forget() )
          // Construct by transferring ownership from another smart pointer.
        {
        }


        // Assignment operators

      nsAutoPtr<T>&
      operator=( T* rhs )
          // assign from a raw pointer (of the right type)
        {
          assign(rhs);
          return *this;
        }

      nsAutoPtr<T>& operator=( nsAutoPtr<T>& rhs )
          // assign by transferring ownership from another smart pointer.
        {
          assign(rhs.forget());
          return *this;
        }

        // Other pointer operators

      T*
      get() const
          /*
            Prefer the implicit conversion provided automatically by
            |operator T*() const|.  Use |get()| _only_ to resolve
            ambiguity.
          */
        {
          return mRawPtr;
        }

      operator T*() const
          /*
            ...makes an |nsAutoPtr| act like its underlying raw pointer
            type  whenever it is used in a context where a raw pointer
            is expected.  It is this operator that makes an |nsAutoPtr|
            substitutable for a raw pointer.

            Prefer the implicit use of this operator to calling |get()|,
            except where necessary to resolve ambiguity.
          */
        {
          return get();
        }

      T*
      forget()
        {
          T* temp = mRawPtr;
          mRawPtr = 0;
          return temp;
        }

      T*
      operator->() const
        {
          NS_PRECONDITION(mRawPtr != 0, "You can't dereference a NULL nsAutoPtr with operator->().");
          return get();
        }

#ifdef CANT_RESOLVE_CPP_CONST_AMBIGUITY
  // broken version for IRIX

      nsAutoPtr<T>*
      get_address() const
          // This is not intended to be used by clients.  See |address_of|
          // below.
        {
          return NS_CONST_CAST(nsAutoPtr<T>*, this);
        }

#else // CANT_RESOLVE_CPP_CONST_AMBIGUITY

      nsAutoPtr<T>*
      get_address()
          // This is not intended to be used by clients.  See |address_of|
          // below.
        {
          return this;
        }

      const nsAutoPtr<T>*
      get_address() const
          // This is not intended to be used by clients.  See |address_of|
          // below.
        {
          return this;
        }

#endif // CANT_RESOLVE_CPP_CONST_AMBIGUITY

    public:
      T&
      operator*() const
        {
          NS_PRECONDITION(mRawPtr != 0, "You can't dereference a NULL nsAutoPtr with operator*().");
          return *get();
        }

      T**
      StartAssignment()
        {
#ifndef NSCAP_FEATURE_INLINE_STARTASSIGNMENT
          return NS_REINTERPRET_CAST(T**, begin_assignment());
#else
          assign(0);
          return NS_REINTERPRET_CAST(T**, &mRawPtr);
#endif
        }
  };

#ifdef CANT_RESOLVE_CPP_CONST_AMBIGUITY

// This is the broken version for IRIX, which can't handle the version below.

template <class T>
inline
nsAutoPtr<T>*
address_of( const nsAutoPtr<T>& aPtr )
  {
    return aPtr.get_address();
  }

#else // CANT_RESOLVE_CPP_CONST_AMBIGUITY

template <class T>
inline
nsAutoPtr<T>*
address_of( nsAutoPtr<T>& aPtr )
  {
    return aPtr.get_address();
  }

template <class T>
inline
const nsAutoPtr<T>*
address_of( const nsAutoPtr<T>& aPtr )
  {
    return aPtr.get_address();
  }

#endif // CANT_RESOLVE_CPP_CONST_AMBIGUITY

template <class T>
class nsAutoPtrGetterTransfers
    /*
      ...

      This class is designed to be used for anonymous temporary objects in the
      argument list of calls that return COM interface pointers, e.g.,

        nsAutoPtr<IFoo> fooP;
        ...->GetTransferedPointer(getter_Transfers(fooP))

      DO NOT USE THIS TYPE DIRECTLY IN YOUR CODE.  Use |getter_Transfers()| instead.

      When initialized with a |nsAutoPtr|, as in the example above, it returns
      a |void**|, a |T**|, or an |nsISupports**| as needed, that the
      outer call (|GetTransferedPointer| in this case) can fill in.

      This type should be a nested class inside |nsAutoPtr<T>|.
    */
  {
    public:
      explicit
      nsAutoPtrGetterTransfers( nsAutoPtr<T>& aSmartPtr )
          : mTargetSmartPtr(aSmartPtr)
        {
          // nothing else to do
        }

      operator void**()
        {
          return NS_REINTERPRET_CAST(void**, mTargetSmartPtr.StartAssignment());
        }

      operator T**()
        {
          return mTargetSmartPtr.StartAssignment();
        }

      T*&
      operator*()
        {
          return *(mTargetSmartPtr.StartAssignment());
        }

    private:
      nsAutoPtr<T>& mTargetSmartPtr;
  };

template <class T>
inline
nsAutoPtrGetterTransfers<T>
getter_Transfers( nsAutoPtr<T>& aSmartPtr )
    /*
      Used around a |nsAutoPtr| when 
      ...makes the class |nsAutoPtrGetterTransfers<T>| invisible.
    */
  {
    return nsAutoPtrGetterTransfers<T>(aSmartPtr);
  }



  // Comparing two |nsAutoPtr|s

template <class T, class U>
inline
NSCAP_BOOL
operator==( const nsAutoPtr<T>& lhs, const nsAutoPtr<U>& rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) == NS_STATIC_CAST(const U*, rhs.get());
  }


template <class T, class U>
inline
NSCAP_BOOL
operator!=( const nsAutoPtr<T>& lhs, const nsAutoPtr<U>& rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) != NS_STATIC_CAST(const U*, rhs.get());
  }


  // Comparing an |nsAutoPtr| to a raw pointer

template <class T, class U>
inline
NSCAP_BOOL
operator==( const nsAutoPtr<T>& lhs, const U* rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) == NS_STATIC_CAST(const U*, rhs);
  }

template <class T, class U>
inline
NSCAP_BOOL
operator==( const U* lhs, const nsAutoPtr<T>& rhs )
  {
    return NS_STATIC_CAST(const U*, lhs) == NS_STATIC_CAST(const T*, rhs.get());
  }

template <class T, class U>
inline
NSCAP_BOOL
operator!=( const nsAutoPtr<T>& lhs, const U* rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) != NS_STATIC_CAST(const U*, rhs);
  }

template <class T, class U>
inline
NSCAP_BOOL
operator!=( const U* lhs, const nsAutoPtr<T>& rhs )
  {
    return NS_STATIC_CAST(const U*, lhs) != NS_STATIC_CAST(const T*, rhs.get());
  }

  // To avoid ambiguities caused by the presence of builtin |operator==|s
  // creating a situation where one of the |operator==| defined above
  // has a better conversion for one argument and the builtin has a
  // better conversion for the other argument, define additional
  // |operator==| without the |const| on the raw pointer.
  // See bug 65664 for details.

#ifndef NSCAP_DONT_PROVIDE_NONCONST_OPEQ
template <class T, class U>
inline
NSCAP_BOOL
operator==( const nsAutoPtr<T>& lhs, U* rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) == NS_CONST_CAST(const U*, rhs);
  }

template <class T, class U>
inline
NSCAP_BOOL
operator==( U* lhs, const nsAutoPtr<T>& rhs )
  {
    return NS_CONST_CAST(const U*, lhs) == NS_STATIC_CAST(const T*, rhs.get());
  }

template <class T, class U>
inline
NSCAP_BOOL
operator!=( const nsAutoPtr<T>& lhs, U* rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) != NS_CONST_CAST(const U*, rhs);
  }

template <class T, class U>
inline
NSCAP_BOOL
operator!=( U* lhs, const nsAutoPtr<T>& rhs )
  {
    return NS_CONST_CAST(const U*, lhs) != NS_STATIC_CAST(const T*, rhs.get());
  }
#endif



  // Comparing an |nsAutoPtr| to |0|

template <class T>
inline
NSCAP_BOOL
operator==( const nsAutoPtr<T>& lhs, NSCAP_Zero* rhs )
    // specifically to allow |smartPtr == 0|
  {
    return NS_STATIC_CAST(const void*, lhs.get()) == NS_REINTERPRET_CAST(const void*, rhs);
  }

template <class T>
inline
NSCAP_BOOL
operator==( NSCAP_Zero* lhs, const nsAutoPtr<T>& rhs )
    // specifically to allow |0 == smartPtr|
  {
    return NS_REINTERPRET_CAST(const void*, lhs) == NS_STATIC_CAST(const void*, rhs.get());
  }

template <class T>
inline
NSCAP_BOOL
operator!=( const nsAutoPtr<T>& lhs, NSCAP_Zero* rhs )
    // specifically to allow |smartPtr != 0|
  {
    return NS_STATIC_CAST(const void*, lhs.get()) != NS_REINTERPRET_CAST(const void*, rhs);
  }

template <class T>
inline
NSCAP_BOOL
operator!=( NSCAP_Zero* lhs, const nsAutoPtr<T>& rhs )
    // specifically to allow |0 != smartPtr|
  {
    return NS_REINTERPRET_CAST(const void*, lhs) != NS_STATIC_CAST(const void*, rhs.get());
  }


#ifdef HAVE_CPP_TROUBLE_COMPARING_TO_ZERO

  // We need to explicitly define comparison operators for `int'
  // because the compiler is lame.

template <class T>
inline
NSCAP_BOOL
operator==( const nsAutoPtr<T>& lhs, int rhs )
    // specifically to allow |smartPtr == 0|
  {
    return NS_STATIC_CAST(const void*, lhs.get()) == NS_REINTERPRET_CAST(const void*, rhs);
  }

template <class T>
inline
NSCAP_BOOL
operator==( int lhs, const nsAutoPtr<T>& rhs )
    // specifically to allow |0 == smartPtr|
  {
    return NS_REINTERPRET_CAST(const void*, lhs) == NS_STATIC_CAST(const void*, rhs.get());
  }

#endif // !defined(HAVE_CPP_TROUBLE_COMPARING_TO_ZERO)

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

// template <class T> class nsAutoArrayPtrGetterTransfers;

template <class T>
class nsAutoArrayPtr
  {
    private:
      void**
      begin_assignment()
        {
          assign(0);
          return NS_REINTERPRET_CAST(void**, &mRawPtr);
        }

      void
      assign( T* newPtr )
        {
          T* oldPtr = mRawPtr;
          mRawPtr = newPtr;
          delete [] oldPtr;
        }

    private:
      T* mRawPtr;

    public:
      typedef T element_type;
      
     ~nsAutoArrayPtr()
        {
          delete [] mRawPtr;
        }

        // Constructors

      nsAutoArrayPtr()
            : mRawPtr(0)
          // default constructor
        {
        }

      nsAutoArrayPtr( T* aRawPtr )
            : mRawPtr(aRawPtr)
          // construct from a raw pointer (of the right type)
        {
        }

      nsAutoArrayPtr( nsAutoArrayPtr<T>& aSmartPtr )
            : mRawPtr( aSmartPtr.forget() )
          // Construct by transferring ownership from another smart pointer.
        {
        }


        // Assignment operators

      nsAutoArrayPtr<T>&
      operator=( T* rhs )
          // assign from a raw pointer (of the right type)
        {
          assign(rhs);
          return *this;
        }

      nsAutoArrayPtr<T>& operator=( nsAutoArrayPtr<T>& rhs )
          // assign by transferring ownership from another smart pointer.
        {
          assign(rhs.forget());
          return *this;
        }

        // Other pointer operators

      T*
      get() const
          /*
            Prefer the implicit conversion provided automatically by
            |operator T*() const|.  Use |get()| _only_ to resolve
            ambiguity.
          */
        {
          return mRawPtr;
        }

      operator T*() const
          /*
            ...makes an |nsAutoArrayPtr| act like its underlying raw pointer
            type  whenever it is used in a context where a raw pointer
            is expected.  It is this operator that makes an |nsAutoArrayPtr|
            substitutable for a raw pointer.

            Prefer the implicit use of this operator to calling |get()|,
            except where necessary to resolve ambiguity.
          */
        {
          return get();
        }

      T*
      forget()
        {
          T* temp = mRawPtr;
          mRawPtr = 0;
          return temp;
        }

      T*
      operator->() const
        {
          NS_PRECONDITION(mRawPtr != 0, "You can't dereference a NULL nsAutoArrayPtr with operator->().");
          return get();
        }

#ifdef CANT_RESOLVE_CPP_CONST_AMBIGUITY
  // broken version for IRIX

      nsAutoArrayPtr<T>*
      get_address() const
          // This is not intended to be used by clients.  See |address_of|
          // below.
        {
          return NS_CONST_CAST(nsAutoArrayPtr<T>*, this);
        }

#else // CANT_RESOLVE_CPP_CONST_AMBIGUITY

      nsAutoArrayPtr<T>*
      get_address()
          // This is not intended to be used by clients.  See |address_of|
          // below.
        {
          return this;
        }

      const nsAutoArrayPtr<T>*
      get_address() const
          // This is not intended to be used by clients.  See |address_of|
          // below.
        {
          return this;
        }

#endif // CANT_RESOLVE_CPP_CONST_AMBIGUITY

    public:
      T&
      operator*() const
        {
          NS_PRECONDITION(mRawPtr != 0, "You can't dereference a NULL nsAutoArrayPtr with operator*().");
          return *get();
        }

      T**
      StartAssignment()
        {
#ifndef NSCAP_FEATURE_INLINE_STARTASSIGNMENT
          return NS_REINTERPRET_CAST(T**, begin_assignment());
#else
          assign(0);
          return NS_REINTERPRET_CAST(T**, &mRawPtr);
#endif
        }
  };

#ifdef CANT_RESOLVE_CPP_CONST_AMBIGUITY

// This is the broken version for IRIX, which can't handle the version below.

template <class T>
inline
nsAutoArrayPtr<T>*
address_of( const nsAutoArrayPtr<T>& aPtr )
  {
    return aPtr.get_address();
  }

#else // CANT_RESOLVE_CPP_CONST_AMBIGUITY

template <class T>
inline
nsAutoArrayPtr<T>*
address_of( nsAutoArrayPtr<T>& aPtr )
  {
    return aPtr.get_address();
  }

template <class T>
inline
const nsAutoArrayPtr<T>*
address_of( const nsAutoArrayPtr<T>& aPtr )
  {
    return aPtr.get_address();
  }

#endif // CANT_RESOLVE_CPP_CONST_AMBIGUITY

template <class T>
class nsAutoArrayPtrGetterTransfers
    /*
      ...

      This class is designed to be used for anonymous temporary objects in the
      argument list of calls that return COM interface pointers, e.g.,

        nsAutoArrayPtr<IFoo> fooP;
        ...->GetTransferedPointer(getter_Transfers(fooP))

      DO NOT USE THIS TYPE DIRECTLY IN YOUR CODE.  Use |getter_Transfers()| instead.

      When initialized with a |nsAutoArrayPtr|, as in the example above, it returns
      a |void**|, a |T**|, or an |nsISupports**| as needed, that the
      outer call (|GetTransferedPointer| in this case) can fill in.

      This type should be a nested class inside |nsAutoArrayPtr<T>|.
    */
  {
    public:
      explicit
      nsAutoArrayPtrGetterTransfers( nsAutoArrayPtr<T>& aSmartPtr )
          : mTargetSmartPtr(aSmartPtr)
        {
          // nothing else to do
        }

      operator void**()
        {
          return NS_REINTERPRET_CAST(void**, mTargetSmartPtr.StartAssignment());
        }

      operator T**()
        {
          return mTargetSmartPtr.StartAssignment();
        }

      T*&
      operator*()
        {
          return *(mTargetSmartPtr.StartAssignment());
        }

    private:
      nsAutoArrayPtr<T>& mTargetSmartPtr;
  };

template <class T>
inline
nsAutoArrayPtrGetterTransfers<T>
getter_Transfers( nsAutoArrayPtr<T>& aSmartPtr )
    /*
      Used around a |nsAutoArrayPtr| when 
      ...makes the class |nsAutoArrayPtrGetterTransfers<T>| invisible.
    */
  {
    return nsAutoArrayPtrGetterTransfers<T>(aSmartPtr);
  }



  // Comparing two |nsAutoArrayPtr|s

template <class T, class U>
inline
NSCAP_BOOL
operator==( const nsAutoArrayPtr<T>& lhs, const nsAutoArrayPtr<U>& rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) == NS_STATIC_CAST(const U*, rhs.get());
  }


template <class T, class U>
inline
NSCAP_BOOL
operator!=( const nsAutoArrayPtr<T>& lhs, const nsAutoArrayPtr<U>& rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) != NS_STATIC_CAST(const U*, rhs.get());
  }


  // Comparing an |nsAutoArrayPtr| to a raw pointer

template <class T, class U>
inline
NSCAP_BOOL
operator==( const nsAutoArrayPtr<T>& lhs, const U* rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) == NS_STATIC_CAST(const U*, rhs);
  }

template <class T, class U>
inline
NSCAP_BOOL
operator==( const U* lhs, const nsAutoArrayPtr<T>& rhs )
  {
    return NS_STATIC_CAST(const U*, lhs) == NS_STATIC_CAST(const T*, rhs.get());
  }

template <class T, class U>
inline
NSCAP_BOOL
operator!=( const nsAutoArrayPtr<T>& lhs, const U* rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) != NS_STATIC_CAST(const U*, rhs);
  }

template <class T, class U>
inline
NSCAP_BOOL
operator!=( const U* lhs, const nsAutoArrayPtr<T>& rhs )
  {
    return NS_STATIC_CAST(const U*, lhs) != NS_STATIC_CAST(const T*, rhs.get());
  }

  // To avoid ambiguities caused by the presence of builtin |operator==|s
  // creating a situation where one of the |operator==| defined above
  // has a better conversion for one argument and the builtin has a
  // better conversion for the other argument, define additional
  // |operator==| without the |const| on the raw pointer.
  // See bug 65664 for details.

#ifndef NSCAP_DONT_PROVIDE_NONCONST_OPEQ
template <class T, class U>
inline
NSCAP_BOOL
operator==( const nsAutoArrayPtr<T>& lhs, U* rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) == NS_CONST_CAST(const U*, rhs);
  }

template <class T, class U>
inline
NSCAP_BOOL
operator==( U* lhs, const nsAutoArrayPtr<T>& rhs )
  {
    return NS_CONST_CAST(const U*, lhs) == NS_STATIC_CAST(const T*, rhs.get());
  }

template <class T, class U>
inline
NSCAP_BOOL
operator!=( const nsAutoArrayPtr<T>& lhs, U* rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) != NS_CONST_CAST(const U*, rhs);
  }

template <class T, class U>
inline
NSCAP_BOOL
operator!=( U* lhs, const nsAutoArrayPtr<T>& rhs )
  {
    return NS_CONST_CAST(const U*, lhs) != NS_STATIC_CAST(const T*, rhs.get());
  }
#endif



  // Comparing an |nsAutoArrayPtr| to |0|

template <class T>
inline
NSCAP_BOOL
operator==( const nsAutoArrayPtr<T>& lhs, NSCAP_Zero* rhs )
    // specifically to allow |smartPtr == 0|
  {
    return NS_STATIC_CAST(const void*, lhs.get()) == NS_REINTERPRET_CAST(const void*, rhs);
  }

template <class T>
inline
NSCAP_BOOL
operator==( NSCAP_Zero* lhs, const nsAutoArrayPtr<T>& rhs )
    // specifically to allow |0 == smartPtr|
  {
    return NS_REINTERPRET_CAST(const void*, lhs) == NS_STATIC_CAST(const void*, rhs.get());
  }

template <class T>
inline
NSCAP_BOOL
operator!=( const nsAutoArrayPtr<T>& lhs, NSCAP_Zero* rhs )
    // specifically to allow |smartPtr != 0|
  {
    return NS_STATIC_CAST(const void*, lhs.get()) != NS_REINTERPRET_CAST(const void*, rhs);
  }

template <class T>
inline
NSCAP_BOOL
operator!=( NSCAP_Zero* lhs, const nsAutoArrayPtr<T>& rhs )
    // specifically to allow |0 != smartPtr|
  {
    return NS_REINTERPRET_CAST(const void*, lhs) != NS_STATIC_CAST(const void*, rhs.get());
  }


#ifdef HAVE_CPP_TROUBLE_COMPARING_TO_ZERO

  // We need to explicitly define comparison operators for `int'
  // because the compiler is lame.

template <class T>
inline
NSCAP_BOOL
operator==( const nsAutoArrayPtr<T>& lhs, int rhs )
    // specifically to allow |smartPtr == 0|
  {
    return NS_STATIC_CAST(const void*, lhs.get()) == NS_REINTERPRET_CAST(const void*, rhs);
  }

template <class T>
inline
NSCAP_BOOL
operator==( int lhs, const nsAutoArrayPtr<T>& rhs )
    // specifically to allow |0 == smartPtr|
  {
    return NS_REINTERPRET_CAST(const void*, lhs) == NS_STATIC_CAST(const void*, rhs.get());
  }

#endif // !defined(HAVE_CPP_TROUBLE_COMPARING_TO_ZERO)


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

// template <class T> class nsRefPtrGetterAddRefs;

template <class T>
class nsRefPtr
  {
    private:

      void
      assign_with_AddRef( T* rawPtr )
        {
          if ( rawPtr )
            rawPtr->AddRef();
          assign_assuming_AddRef(rawPtr);
        }

      void**
      begin_assignment()
        {
          assign_assuming_AddRef(0);
          return NS_REINTERPRET_CAST(void**, &mRawPtr);
        }

      void
      assign_assuming_AddRef( T* newPtr )
        {
          T* oldPtr = mRawPtr;
          mRawPtr = newPtr;
          if ( oldPtr )
            oldPtr->Release();
        }

    private:
      T* mRawPtr;

    public:
      typedef T element_type;
      
     ~nsRefPtr()
        {
          if ( mRawPtr )
            mRawPtr->Release();
        }

        // Constructors

      nsRefPtr()
            : mRawPtr(0)
          // default constructor
        {
        }

      nsRefPtr( const nsRefPtr<T>& aSmartPtr )
            : mRawPtr(aSmartPtr.mRawPtr)
          // copy-constructor
        {
          if ( mRawPtr )
            mRawPtr->AddRef();
        }

      nsRefPtr( T* aRawPtr )
            : mRawPtr(aRawPtr)
          // construct from a raw pointer (of the right type)
        {
          if ( mRawPtr )
            mRawPtr->AddRef();
        }

      nsRefPtr( const already_AddRefed<T>& aSmartPtr )
            : mRawPtr(aSmartPtr.mRawPtr)
          // construct from |dont_AddRef(expr)|
        {
        }

        // Assignment operators

      nsRefPtr<T>&
      operator=( const nsRefPtr<T>& rhs )
          // copy assignment operator
        {
          assign_with_AddRef(rhs.mRawPtr);
          return *this;
        }

      nsRefPtr<T>&
      operator=( T* rhs )
          // assign from a raw pointer (of the right type)
        {
          assign_with_AddRef(rhs);
          return *this;
        }

      nsRefPtr<T>&
      operator=( const already_AddRefed<T>& rhs )
          // assign from |dont_AddRef(expr)|
        {
          assign_assuming_AddRef(rhs.mRawPtr);
          return *this;
        }

        // Other pointer operators

      void
      swap( nsRefPtr<T>& rhs )
          // ...exchange ownership with |rhs|; can save a pair of refcount operations
        {
          T* temp = rhs.mRawPtr;
          rhs.mRawPtr = mRawPtr;
          mRawPtr = temp;
        }

      void
      swap( T*& rhs )
          // ...exchange ownership with |rhs|; can save a pair of refcount operations
        {
          T* temp = rhs;
          rhs = mRawPtr;
          mRawPtr = temp;
        }

      nsDerivedSafe<T>*
      get() const
          /*
            Prefer the implicit conversion provided automatically by |operator nsDerivedSafe<T>*() const|.
             Use |get()| _only_ to resolve ambiguity.

            Returns a |nsDerivedSafe<T>*| to deny clients the use of |AddRef| and |Release|.
          */
        {
          return NS_CONST_CAST(nsDerivedSafe<T>*,
                        NS_REINTERPRET_CAST(const nsDerivedSafe<T>*, mRawPtr));
        }

      operator nsDerivedSafe<T>*() const
          /*
            ...makes an |nsRefPtr| act like its underlying raw pointer type (except against |AddRef()|, |Release()|,
              and |delete|) whenever it is used in a context where a raw pointer is expected.  It is this operator
              that makes an |nsRefPtr| substitutable for a raw pointer.

            Prefer the implicit use of this operator to calling |get()|, except where necessary to resolve ambiguity.
          */
        {
          return get();
        }

      nsDerivedSafe<T>*
      operator->() const
        {
          NS_PRECONDITION(mRawPtr != 0, "You can't dereference a NULL nsRefPtr with operator->().");
          return get();
        }

#ifdef CANT_RESOLVE_CPP_CONST_AMBIGUITY
  // broken version for IRIX

      nsRefPtr<T>*
      get_address() const
          // This is not intended to be used by clients.  See |address_of|
          // below.
        {
          return NS_CONST_CAST(nsRefPtr<T>*, this);
        }

#else // CANT_RESOLVE_CPP_CONST_AMBIGUITY

      nsRefPtr<T>*
      get_address()
          // This is not intended to be used by clients.  See |address_of|
          // below.
        {
          return this;
        }

      const nsRefPtr<T>*
      get_address() const
          // This is not intended to be used by clients.  See |address_of|
          // below.
        {
          return this;
        }

#endif // CANT_RESOLVE_CPP_CONST_AMBIGUITY

    public:
      nsDerivedSafe<T>&
      operator*() const
        {
          NS_PRECONDITION(mRawPtr != 0, "You can't dereference a NULL nsRefPtr with operator*().");
          return *get();
        }

      T**
      StartAssignment()
        {
#ifndef NSCAP_FEATURE_INLINE_STARTASSIGNMENT
          return NS_REINTERPRET_CAST(T**, begin_assignment());
#else
          assign_assuming_AddRef(0);
          return NS_REINTERPRET_CAST(T**, &mRawPtr);
#endif
        }
  };

#ifdef CANT_RESOLVE_CPP_CONST_AMBIGUITY

// This is the broken version for IRIX, which can't handle the version below.

template <class T>
inline
nsRefPtr<T>*
address_of( const nsRefPtr<T>& aPtr )
  {
    return aPtr.get_address();
  }

#else // CANT_RESOLVE_CPP_CONST_AMBIGUITY

template <class T>
inline
nsRefPtr<T>*
address_of( nsRefPtr<T>& aPtr )
  {
    return aPtr.get_address();
  }

template <class T>
inline
const nsRefPtr<T>*
address_of( const nsRefPtr<T>& aPtr )
  {
    return aPtr.get_address();
  }

#endif // CANT_RESOLVE_CPP_CONST_AMBIGUITY

template <class T>
class nsRefPtrGetterAddRefs
    /*
      ...

      This class is designed to be used for anonymous temporary objects in the
      argument list of calls that return COM interface pointers, e.g.,

        nsRefPtr<IFoo> fooP;
        ...->GetAddRefedPointer(getter_AddRefs(fooP))

      DO NOT USE THIS TYPE DIRECTLY IN YOUR CODE.  Use |getter_AddRefs()| instead.

      When initialized with a |nsRefPtr|, as in the example above, it returns
      a |void**|, a |T**|, or an |nsISupports**| as needed, that the
      outer call (|GetAddRefedPointer| in this case) can fill in.

      This type should be a nested class inside |nsRefPtr<T>|.
    */
  {
    public:
      explicit
      nsRefPtrGetterAddRefs( nsRefPtr<T>& aSmartPtr )
          : mTargetSmartPtr(aSmartPtr)
        {
          // nothing else to do
        }

      operator void**()
        {
          return NS_REINTERPRET_CAST(void**, mTargetSmartPtr.StartAssignment());
        }

      operator T**()
        {
          return mTargetSmartPtr.StartAssignment();
        }

      T*&
      operator*()
        {
          return *(mTargetSmartPtr.StartAssignment());
        }

    private:
      nsRefPtr<T>& mTargetSmartPtr;
  };

template <class T>
inline
nsRefPtrGetterAddRefs<T>
getter_AddRefs( nsRefPtr<T>& aSmartPtr )
    /*
      Used around a |nsRefPtr| when 
      ...makes the class |nsRefPtrGetterAddRefs<T>| invisible.
    */
  {
    return nsRefPtrGetterAddRefs<T>(aSmartPtr);
  }



  // Comparing two |nsRefPtr|s

template <class T, class U>
inline
NSCAP_BOOL
operator==( const nsRefPtr<T>& lhs, const nsRefPtr<U>& rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) == NS_STATIC_CAST(const U*, rhs.get());
  }


template <class T, class U>
inline
NSCAP_BOOL
operator!=( const nsRefPtr<T>& lhs, const nsRefPtr<U>& rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) != NS_STATIC_CAST(const U*, rhs.get());
  }


  // Comparing an |nsRefPtr| to a raw pointer

template <class T, class U>
inline
NSCAP_BOOL
operator==( const nsRefPtr<T>& lhs, const U* rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) == NS_STATIC_CAST(const U*, rhs);
  }

template <class T, class U>
inline
NSCAP_BOOL
operator==( const U* lhs, const nsRefPtr<T>& rhs )
  {
    return NS_STATIC_CAST(const U*, lhs) == NS_STATIC_CAST(const T*, rhs.get());
  }

template <class T, class U>
inline
NSCAP_BOOL
operator!=( const nsRefPtr<T>& lhs, const U* rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) != NS_STATIC_CAST(const U*, rhs);
  }

template <class T, class U>
inline
NSCAP_BOOL
operator!=( const U* lhs, const nsRefPtr<T>& rhs )
  {
    return NS_STATIC_CAST(const U*, lhs) != NS_STATIC_CAST(const T*, rhs.get());
  }

  // To avoid ambiguities caused by the presence of builtin |operator==|s
  // creating a situation where one of the |operator==| defined above
  // has a better conversion for one argument and the builtin has a
  // better conversion for the other argument, define additional
  // |operator==| without the |const| on the raw pointer.
  // See bug 65664 for details.

#ifndef NSCAP_DONT_PROVIDE_NONCONST_OPEQ
template <class T, class U>
inline
NSCAP_BOOL
operator==( const nsRefPtr<T>& lhs, U* rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) == NS_CONST_CAST(const U*, rhs);
  }

template <class T, class U>
inline
NSCAP_BOOL
operator==( U* lhs, const nsRefPtr<T>& rhs )
  {
    return NS_CONST_CAST(const U*, lhs) == NS_STATIC_CAST(const T*, rhs.get());
  }

template <class T, class U>
inline
NSCAP_BOOL
operator!=( const nsRefPtr<T>& lhs, U* rhs )
  {
    return NS_STATIC_CAST(const T*, lhs.get()) != NS_CONST_CAST(const U*, rhs);
  }

template <class T, class U>
inline
NSCAP_BOOL
operator!=( U* lhs, const nsRefPtr<T>& rhs )
  {
    return NS_CONST_CAST(const U*, lhs) != NS_STATIC_CAST(const T*, rhs.get());
  }
#endif



  // Comparing an |nsRefPtr| to |0|

template <class T>
inline
NSCAP_BOOL
operator==( const nsRefPtr<T>& lhs, NSCAP_Zero* rhs )
    // specifically to allow |smartPtr == 0|
  {
    return NS_STATIC_CAST(const void*, lhs.get()) == NS_REINTERPRET_CAST(const void*, rhs);
  }

template <class T>
inline
NSCAP_BOOL
operator==( NSCAP_Zero* lhs, const nsRefPtr<T>& rhs )
    // specifically to allow |0 == smartPtr|
  {
    return NS_REINTERPRET_CAST(const void*, lhs) == NS_STATIC_CAST(const void*, rhs.get());
  }

template <class T>
inline
NSCAP_BOOL
operator!=( const nsRefPtr<T>& lhs, NSCAP_Zero* rhs )
    // specifically to allow |smartPtr != 0|
  {
    return NS_STATIC_CAST(const void*, lhs.get()) != NS_REINTERPRET_CAST(const void*, rhs);
  }

template <class T>
inline
NSCAP_BOOL
operator!=( NSCAP_Zero* lhs, const nsRefPtr<T>& rhs )
    // specifically to allow |0 != smartPtr|
  {
    return NS_REINTERPRET_CAST(const void*, lhs) != NS_STATIC_CAST(const void*, rhs.get());
  }


#ifdef HAVE_CPP_TROUBLE_COMPARING_TO_ZERO

  // We need to explicitly define comparison operators for `int'
  // because the compiler is lame.

template <class T>
inline
NSCAP_BOOL
operator==( const nsRefPtr<T>& lhs, int rhs )
    // specifically to allow |smartPtr == 0|
  {
    return NS_STATIC_CAST(const void*, lhs.get()) == NS_REINTERPRET_CAST(const void*, rhs);
  }

template <class T>
inline
NSCAP_BOOL
operator==( int lhs, const nsRefPtr<T>& rhs )
    // specifically to allow |0 == smartPtr|
  {
    return NS_REINTERPRET_CAST(const void*, lhs) == NS_STATIC_CAST(const void*, rhs.get());
  }

#endif // !defined(HAVE_CPP_TROUBLE_COMPARING_TO_ZERO)

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

#endif // !defined(nsAutoPtr_h___)
