/*----------------------------------------------------------------*/
/* This file can be saved to your machine (almost) as you see it  */
/* by doing a "Save As..." from the File menu of your browser.    */
/*                                                                */
/* For this to work, you must TYPE a filename with .txt as the    */
/* file extent.  (Simply selecting .txt as the file type          */
/* doesn't seem to be sufficient.)  Remove the .txt extension     */
/* once the file is saved.  (IE inserts the page title as the     */
/* first line, which must be removed also.)                       */
/*----------------------------------------------------------------*/

// Author: Hannah C. Tang (hctang@cs)
//
// Implementation for a templated array class with bounds checking


#include <iostream>
#include <cassert>
using namespace std;

#include "Array.hh"

//
// Overloaded constructor.  Note the use of the member initialization list
// to initialize m_capacity before its use in the body of the constructor
//
template< typename T >
Array< T >::Array( int initCapacity )
    : m_capacity( initCapacity )
{
    m_pData = new T[ m_capacity ];
}


//
// Copy constructor.  Uses the member function Copy().  Note that there
// is no need to delete m_pData prior to setting it to NULL in the 
// memberwise initialization list.  Why do you think this is the case?
//
template< typename T >
Array< T >::Array( const Array& other )
    : m_pData( NULL ) 
{
    Copy( other );
}


//
// Assignment operator.  Uses the member functions Copy() and Destroy().
// Remember that it is necessary to check against self-assignment. 
// Why does the assignment operator need to delete m_pData but the
// copy constructor does not?
//
template< typename T >
const Array< T >&
Array< T >::operator=( const Array& other )
{
    if( this != &other )
    {
        Destroy();       // Delete existing memory
        Copy( other );   // Copy 'other' into 'this'
    }
    return *this;
}


//
// Destructor.  Uses the member function Destroy()
//
template< typename T >
Array< T >::~Array( void )
{
    Destroy();
}


//
// Indexing operator, const version
//
template< typename T >
const T&
Array< T >::operator[]( int index ) const
{
    // Invalid index.  Note that I'm using >=, not >
    // Asserting a "NOT'ed string" will always be false, but
    // also allows for an informative error message
    assert( index >= 0 && index < m_capacity 
            && "Index out of bounds on operator[]!" );

    return m_pData[ index ];
}


//
// Indexing operator, non-const version
// 
template< typename T >
T&
Array< T >::operator[]( int index )
{
    // Invalid index.  Note that I'm using >=, not >
    if( index < 0 || index >= m_capacity )
    {
        // Asserting "NOT'ed string" will always be false but
        // also allows for an informative error message
        assert( index >= 0 && index < m_capacity 
                && "Index out of bounds on operator[]!" );
    }

    return m_pData[ index ];
}


//
// GetCapacity.  Gets the current capacity of the array
//
template< typename T >
int
Array< T >::GetCapacity( void )
{
    return m_capacity;
}


//
// Copy.  Copies its argument into the current instance
//
template< typename T >
void
Array< T >::Copy( const Array& other )
{
    m_capacity = other.m_capacity;

    // Create an array that is the same size as 'other', and
    // copy other's data over
    m_pData = new T[ m_capacity ];

    for( int i = 0; i < m_capacity; i++ )
    {
        m_pData[ i ] = other.m_pData[ i ];
    }
}


//
// Destroy.  Cleans up the current instance (deletes dynamic memory, etc)
//
template< typename T >
void
Array< T >::Destroy( void )
{
    delete [] m_pData; // C++ trivia: deleting NULL is defined to be a no-op
                       // Note that I'm using delete[], not delete.  Remember:
                       // if you use new[], it *must* be matched with delete[]
    m_pData = NULL;
    m_capacity = 0;
}