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

#include "FlexArray.h"
#include <cstdlib>

FlexArrayBase::~FlexArrayBase()
{
    clear();
    if( 0 != array_ )
    {
        delete [] array_;
        array_ = 0;
    }
};

void FlexArrayBase::clear()
{
    while( maxIndex_ >= minIndex_ )
    {
        if( deleteOnCleanup_ )
        {
            freeItem( ( void* )array_[maxIndex_] );
        }
        array_[maxIndex_] = ( void* )0;
        --maxIndex_;
    }
};

void FlexArrayBase::freeItem( void* item )
{
    if( item )
    {
        free( item );
    }
}
int FlexArrayBase::getMinIndex() const
{
    return maxIndex_ == -1 ? 0 : minIndex_;
};

int FlexArrayBase::getMaxIndex() const
{
    return maxIndex_;
};

int FlexArrayBase::find( void* item ) const
{
    for( int i = minIndex_; i <= maxIndex_; ++i )
    {
        if( item == array_[i] )
        {
            return i;
        }
    }
    return -1;
};

bool FlexArrayBase::isEmpty() const
{
    return maxIndex_ < minIndex_;
}

unsigned int FlexArrayBase::size() const
{
    return isEmpty() ? 0 : maxIndex_ - minIndex_ + 1;
}

FlexArrayBase::FlexArrayBase( const bool deleteOnCleanup,
                              const unsigned int initialCapacity /*=32*/,
                              const unsigned int maxCapacity /*=16384*/ )
    : initialCapacity_( initialCapacity ),
      size_( 0 ),
      deleteOnCleanup_( deleteOnCleanup ),
      maxCapacity_( maxCapacity ),
      minIndex_( maxCapacity ),
      maxIndex_( -1 ),
      array_( 0 )
{
}

void FlexArrayBase::set( const unsigned int index, const void* item )
{
    int setIndex = this->resolveIndex( index );
    if( -1 != setIndex )
    {
        array_[setIndex] = item;
    }
}

int FlexArrayBase::getIndex( int index/*= -1*/ ) const
{
    if( index < 0 )
    {
        index = maxIndex_ + 1 + index;
    }
    if( index >= 0 && index < ( int )size_ )
    {
        return index;
    }
    return -1;
}

void FlexArrayBase::insert( const unsigned int index, void* item )
{
    for( int i = maxIndex_; i >= ( int )index; --i )
    {
        this->set( i + 1, array_[ i ] );
    }
    this->set( index, item );
}

void FlexArrayBase::push( void* item )
{
    insert( maxIndex_ + 1, item );
}

const void* FlexArrayBase::peek( int index /*= -1*/ )
{
    if( index < 0 )
    {
        index = maxIndex_ + 1 + index;
    }
    if( index >= 0 )
    {
        int itemIndex = this->resolveIndex( index );
        if( 0 <= itemIndex )
        {
            return array_[ itemIndex ];
        }
    }
    return NULL;
}

const void* FlexArrayBase::pop( int index /*= -1*/ )
{
    if( index < 0 )
    {
        index = maxIndex_ + 1 + index;
    }
    if( index >= 0 )
    {
        int itemIndex = this->resolveIndex( index );
        if( 0 <= itemIndex )
        {
            const void* item = array_[ itemIndex ];
            for( int i = ( int )index; i < maxIndex_; ++i )
            {
                array_[ i ] = array_[ i + 1];
            }
            array_[ maxIndex_ ] = 0;
            --maxIndex_;
            return item;
        }
    }
    return NULL;
}

int FlexArrayBase::resolveIndex( const unsigned int index )
{
    unsigned int newSize = size_ == 0 ? 1 : size_;
    while( newSize <= index  && ( newSize << 1 ) <= maxCapacity_ )
    {
        newSize <<= 1;
    }
    if( newSize > size_ )
    {
        setArraySize( newSize );
    }
    if( index < size_ )
    {
        if( minIndex_ > ( int )index )
        {
            minIndex_ = index;
        }
        if( maxIndex_ < ( int )index )
        {
            maxIndex_ = index;
        }
        return index;
    }
    return -1;
};

void FlexArrayBase::setArraySizeBase( const unsigned int newSize )
{
    const void** newArray = new const void*[ newSize ];
    unsigned int i = 0;
    for( ; i < size_; ++i )
    {
        newArray[ i ] = array_[i];
    }
    for( ; i < newSize; ++i )
    {
        newArray[ i ] = ( void* )0;
    }
    if( size_ > 0 )
    {
        delete[] array_;
    }
    size_ = newSize;
    array_ = newArray;
};
