/** \file
 *
 *  Contains the FlexArrayBase and FlexArray class declarations.
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 */

#ifndef FLEXARRAY_H_
#define FLEXARRAY_H_

#include <cstdio>
#include <typeinfo>

/**
 *  Simple class providing a flexible size array of void*.
 *  If you exceed the array limits, it doubles itself in size.
 *  It can act as an array, a list, or a stack
 *
 *  \ingroup utils
 */
class FlexArrayBase
{
public:

    FlexArrayBase( const bool deleteOnCleanup,
                   const unsigned int initialCapacity = 32,
                   const unsigned int maxCapacity = 16384 );

    virtual ~FlexArrayBase();

    void clear();

    int getMinIndex() const;

    int getMaxIndex() const;

    bool isEmpty() const;

    unsigned int size() const;

    void set( const unsigned int index, const void* item );

    int find( void* item ) const;

    void insert( const unsigned int index, void* item );

    void push( void* item );

    const void* peek( int index = -1 );

    const void* pop( int index = -1 );

protected:

    virtual void freeItem( void* item );

    int getIndex( int index = -1 ) const;

    int resolveIndex( const unsigned int index );

    virtual void setArraySize( const unsigned int newSize )
    {
        setArraySizeBase( newSize );
    }

    void setArraySizeBase( const unsigned int newSize );

    const unsigned int initialCapacity_;
    unsigned int size_;
    const bool deleteOnCleanup_;
    const unsigned int maxCapacity_;
    int minIndex_;
    int maxIndex_;

    const void** array_;

private:
    // Note that the copy constructor below is private and not given a body.
    // Any attempt to call it will return a compiler error.
    FlexArrayBase( const FlexArrayBase &old ); // disallow copy constructor

};

/**
 *  Simple class providing a flexible size array of pointers.
 *  If you exceed the array limits, it doubles itself in size.
 *  It can act as an array, a list, or a stack
 *
 *  \ingroup utils
 */
template <typename T>
class FlexArray : public FlexArrayBase
{
public:
    FlexArray( const bool deleteOnCleanup,
               const unsigned int initialCapacity = 32,
               const unsigned int maxCapacity = 16384 )
        : FlexArrayBase( deleteOnCleanup, initialCapacity, maxCapacity ),
          nullItem_( ( T )0 ),
          tArray_( 0 ),
          doArrayDelete_( false )
    {
        setArraySize( initialCapacity_ );
    };

    ~FlexArray()
    {
        // This duplicates the call in ~FlexarrayBase, but manages to use
        // the local version of freeItem()
        clear();
    }

    void freeItem( void* item )
    {
        if( item )
        {
            if( doArrayDelete_ )
            {
                delete[]( T )item;
            }
            else
            {
                delete( T )item;
            }
        }
    }

    void set( const unsigned int index, const T& item )
    {
        FlexArrayBase::set( index, ( void* ) item );
    }

    const T& get( int index = -1 ) const
    {
        int itemIndex = FlexArrayBase::getIndex( index );
        if( 0 > itemIndex )
        {
            return nullItem_;
        }
        return tArray_[ itemIndex ];
    };

    T& operator[]( const unsigned int index )
    {
        int itemIndex = FlexArrayBase::resolveIndex( index );
        return 0 > itemIndex ? nullItem_ : tArray_[ itemIndex ];
    };

    int find( T item )
    {
        return FlexArrayBase::find( item );
    }

    void insert( const unsigned int index, T item )
    {
        FlexArrayBase::insert( index, item );
    }

    void push( T item )
    {
        FlexArrayBase::push( const_cast<void*>( ( const void* )item ) );
    }

    T peek( int index = -1 )
    {
        const void* item = FlexArrayBase::peek( index );
        return NULL == item ? nullItem_ : ( T )item;
    }

    T pop( int index = -1 )
    {
        const void* item = FlexArrayBase::pop( index );
        return NULL == item ? nullItem_ : ( T )item;
    }

    T pop( T item )
    {
        int itemIndex = FlexArrayBase::find( item );
        return 0 > itemIndex ? nullItem_ : pop( itemIndex );
    }

    void setArraySize( const unsigned int newSize )
    {
        setArraySizeBase( newSize );
        tArray_ = ( T* )array_;
    }

    T* getArray()
    {
        return tArray_;
    }

    void setDoArrayDelete( bool doArrayDelete )
    {
        doArrayDelete_ = doArrayDelete;
    }

protected:

    T nullItem_;
    T* tArray_;
    bool doArrayDelete_;

private:
    // Note that the copy constructor below is private and not given a body.
    // Any attempt to call it will return a compiler error.
    FlexArray( const FlexArray<T>& old ); // disallow copy constructor

};

#endif /*FLEXARRAY_H_*/
