// ------------------------------------------------------------------------
/*
	author: Jaap Suter
   
	Copyright (C) Electronic Arts Canada Inc. 2002.  All rights reserved.	
*/
// ------------------------------------------------------------------------

/*! \file */

#ifndef REVEAL_SCOPED_ARRAY_H
#define REVEAL_SCOPED_ARRAY_H

#include <assert.h>

#include <stddef.h> // For size_t

namespace Reveal {
    namespace SmartPtr {
    
// ------------------------------------------------------------------------
/*!
    \brief  Single-ownership, non-copyable, non-transferable smart pointer to an array.

    \param T    Type of the object pointed to

    Holds a pointer to a dynamic array (allocated with operator new), and 
    garantuees destruction of the array either upon destruction of the ScopedArray
    itself, or through an explicit reset. 
    
    \warning    Do NOT use this ScopedArray for single objects, as this will result in 
                undefined behaviour. This is because the destructor uses 
                operator delete[] and not operator delete. 

    \remark     This smart pointer is modeled after boost::scoped_array. See 
                http://www.boost.org/libs/smart_ptr/scoped_array.htm for more information, a 
                rationale, and more discussions.
*/
// ------------------------------------------------------------------------
template < class T >
class ScopedArray
{
public:

    /*!
        \param  array Pointer to first element of an array instantiated with operator new.
        \post   a\ mArray = \a array and \a this owns \a mArray
        
        This constructor acts as default constructor too because the 
        parameter has a default 0 value;
    */
    ScopedArray( T* array = 0 ) : mArray( array ) {}

    /*!
        \post  \a mArray is destructed en deleted
    */    
    ~ScopedArray() 
    { 
        // Compile-time check to make sure we're deleting a complete complete type;
        // Will fail if the type is incomplete because zero-sized arrays are not allowed;
        typedef char typeMustBeComplete[ sizeof( T ) ];   
        
        delete [] mArray;
    }

    /*!
        \brief  Const indexing Operator.
		\param  \a i Index, no range checking is performed;
        \return \a mArray[ i ]
        \pre    \a mArray != 0 (asserted)
    */
    const T& operator [] ( size_t i ) const { assert( mArray ); return mArray[ i ]; }

	//! Non-const version;
	T& operator [] ( size_t i ) { assert( mArray ); return mArray[ i ]; }

    /*!
        \brief Swaps two arrays;
        \pre   this points to array A and scopedPtr points to array B
        \post  this points to array B and scopedPtr points to array A
    */
    void Swap( ScopedArray& scopedArray )
    {
        T* tmp = scopedArray.mArray;
        scopedArray.mArray = mArray;
        mArray = tmp;
    }
    
    /*!
        \brief Gives the scoped array a new value, deleting the old one.
        \param array new value for the poiner
        \post  What \a mArray used to point to is deleted and \a mArray == \a array
    */ 
    void Reset( T* array = 0 )
    {
        delete [] mArray;
        mArray = array;
    }

    /*!
        \brief Gets the actual raw pointer to the first element of the array;
        \return \a mArray
        
        This is usefull for interfacing with code that expects raw ptrs (without ownership), for 
        example for weak/observe purposes.

        \warning Don't delete the pointer that is returned, because this ScopedARray is still
                 the owner of the resource. Thus, the results of the following code are 
                 undefined:
                 \code
                    ScopedArray< int > i( new int[ 1 ] );
                    int* p = i;
                    delete [] p;
                 \endcode
    */
	T* Get() const { return mArray; }

private:

    //! Copy ctor disabled, not implemented;
    ScopedArray( const ScopedArray& );

    //! Assigment operator disabled, not implemented;
    ScopedArray& operator = ( const ScopedArray& );

    //! Actual raw pointer;
    T* mArray;
};

    } // Namespace SmartPtr
} // Namespace Reveal

#endif
