/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * 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 strings.
 * 
 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 2000 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s):
 *   Scott Collins <scc@mozilla.org> (original author)
 *
 */

#ifndef nsBufferHandle_h___
#define nsBufferHandle_h___

#include <stddef.h>
  // for |ptrdiff_t|

#include "prtypes.h"
  // for |PRBool|

#include "nsDebug.h"
  // for |NS_ASSERTION|

#include "nsMemory.h"
  // for |nsMemory::Free|

  /**
   *
   */
template <class CharT>
class nsBufferHandle
  {
    public:
      nsBufferHandle( CharT* aDataStart, CharT* aDataEnd ) : mDataStart(aDataStart), mDataEnd(aDataEnd) { }

      void          DataStart( CharT* aNewDataStart )       { mDataStart = aNewDataStart; }
      CharT*        DataStart()                             { return mDataStart; }
      const CharT*  DataStart() const                       { return mDataStart; }

      void          DataEnd( CharT* aNewDataEnd )           { mDataEnd = aNewDataEnd; }
      CharT*        DataEnd()                               { return mDataEnd; }
      const CharT*  DataEnd() const                         { return mDataEnd; }

//    void          DataLength( ptrdiff_t aNewDataLength )  { mDataEnd = mDataStart+aNewDataLength; }
      ptrdiff_t     DataLength() const                      { return mDataEnd - mDataStart; }

    protected:
      CharT*  mDataStart;
      CharT*  mDataEnd;
  };



  /**
   *
   */
template <class CharT>
class nsSharedBufferHandle
    : public nsBufferHandle<CharT>
  {
    protected:
      enum
        {
          kIsShared                       = 1<<31,
          kIsSingleAllocationWithBuffer   = 1<<30,
          kIsStorageDefinedSeparately     = 1<<29,

          kFlagsMask                      = kIsShared | kIsSingleAllocationWithBuffer | kIsStorageDefinedSeparately,
          kRefCountMask                   = ~kFlagsMask
        };

    public:
      nsSharedBufferHandle( CharT* aDataStart, CharT* aDataEnd )
          : nsBufferHandle<CharT>(aDataStart, aDataEnd)
        {
          mFlags = kIsShared;
        }

      ~nsSharedBufferHandle();

      void
      AcquireReference() const
        {
          nsSharedBufferHandle<CharT>* mutable_this = NS_CONST_CAST(nsSharedBufferHandle<CharT>*, this);
          mutable_this->set_refcount( get_refcount()+1 );
        }

      void
      ReleaseReference() const
        {
          nsSharedBufferHandle<CharT>* mutable_this = NS_CONST_CAST(nsSharedBufferHandle<CharT>*, this);
          if ( !mutable_this->set_refcount( get_refcount()-1 ) )
            delete mutable_this;
        }

      PRBool
      IsReferenced() const
        {
          return get_refcount() != 0;
        }

    protected:
      PRUint32  mFlags;

      PRUint32
      get_refcount() const
        {
          return mFlags & kRefCountMask;
        }

      PRUint32
      set_refcount( PRUint32 aNewRefCount )
        {
          NS_ASSERTION(aNewRefCount <= kRefCountMask, "aNewRefCount <= kRefCountMask");

          mFlags = (mFlags & kFlagsMask) | aNewRefCount;
          return aNewRefCount;
        }
  };


  // need a name for this
template <class CharT>
class nsFlexBufferHandle
    : public nsSharedBufferHandle<CharT>
  {
    public:
      nsFlexBufferHandle( CharT* aDataStart, CharT* aDataEnd, CharT* aStorageStart, CharT* aStorageEnd )
          : nsSharedBufferHandle<CharT>(aDataStart, aDataEnd),
            mStorageStart(aStorageStart),
            mStorageEnd(aStorageEnd)
        {
          this->mFlags |= this->kIsStorageDefinedSeparately;
        }

      void          StorageStart( CharT* aNewStorageStart )       { mStorageStart = aNewStorageStart; }
      CharT*        StorageStart()                                { return mStorageStart; }
      const CharT*  StorageStart() const                          { return mStorageStart; }

      void          StorageEnd( CharT* aNewStorageEnd )           { mStorageEnd = aNewStorageEnd; }
      CharT*        StorageEnd()                                  { return mStorageEnd; }
      const CharT*  StorageEnd() const                            { return mStorageEnd; }

//    void          StorageLength( ptrdiff_t aNewStorageLength )  { mStorageEnd = mStorageStart+aNewStorageLength; }
      ptrdiff_t     StorageLength() const                         { return mStorageEnd - mStorageStart; }

    protected:
      CharT*  mStorageStart;
      CharT*  mStorageEnd;
  };

template <class CharT>
nsSharedBufferHandle<CharT>::~nsSharedBufferHandle()
    // really don't want this to be |inline|
  {
    NS_ASSERTION(!IsReferenced(), "!IsReferenced()");

    if ( !(mFlags & kIsSingleAllocationWithBuffer) )
      {
        CharT* string_storage = this->mDataStart;
        if ( mFlags & kIsStorageDefinedSeparately )
          string_storage = NS_REINTERPRET_CAST(nsFlexBufferHandle<CharT>*, this)->StorageStart();
        nsMemory::Free(string_storage);
      }
  }


#endif // !defined(nsBufferHandle_h___)
