/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   SmallArray class.
 *//*
 * LEGAL:   COPYRIGHT (C) 2007 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#ifndef BASE_ARRAY_SMALL_HH
#define BASE_ARRAY_SMALL_HH 1

#include "base/types.hh"

namespace base {

////////////////////////////////////////////////////////////////////////////////
/// @brief Pretends to have a variable-size (but underlying array is fixed-size).
///
/// Limited-use (for speed/memory optimization).
///
/// Like TinyArray, SmallArray's underlying array really is fixed-size, but SmallArray
/// pretends to have variable-size which allows push_back(), size(), resize().
/// SmallArray's advantage over STL vector is reduced memory consumption and allocation.
///
/// NOTE: resize() will fail if new size is beyond fixed-size.
///
/// Usage:
/// ------
/// This pre-allocates space for 256 elements (FIXED-SIZE).
/// But its VARIABLE-SIZE is zero: empty() and size() indicate zero elements!
///
///     SmallArray<256,int> array;
///     if ( array.empty() ) Func();  // yes, Func() will be called
///
/// Idiom:
///     SmallArray<1000,int> array( 1000 );  // fixed-size = variable-size
///
template<uint FIXED_SIZE, typename T=int>
class SmallArray
{
public:
    typedef T Type;  // type of element

public:
    SmallArray( const uint varSize = 0 )
    :   mVarSize(varSize)
    {
        // NOP
    }

    /// Copy constructor.
    SmallArray( const SmallArray<FIXED_SIZE,T>& src )
    {
        *this = src;  // invoke operator=()
    }

    /// Catching access to an uninitialized element
    /// would be ideal but there's no way to tell
    /// if the element is being read or written.
    T& operator[]( uint i )                 // compiled unless const
    {
    ASSERT( (i < mVarSize) and (i < FIXED_SIZE) );
        return mArray[i];
    }

    const T& operator[]( uint i ) const     // compiled if const
    {
    ASSERT( (i < mVarSize) and (i < FIXED_SIZE) );
        return mArray[i];
    }

    /// Amazing that a C++ compiler will accept how this type is written.
    SmallArray<FIXED_SIZE,T>& operator=( const SmallArray<FIXED_SIZE,T>& src )
    {
        mVarSize = src.mVarSize;                    // assign variable-size
        for ( uint i = 0; i < src.mVarSize; ++i )   // copying all elements isn't necessary
        {
            mArray[i] = src.mArray[i];
        }
        return *this;
    }

    bool operator==( const SmallArray<FIXED_SIZE,T>& src ) const
    {
        if ( mVarSize == src.mVarSize )
        {
            for ( uint i = 0; i < mVarSize; ++i )
            {
                if ( mArray[i] != src.mArray[i] )
                    return false;  // different
            }
            return true;  // all elements equal
        }
        else
        {
            return false;  // different sizes
        }
    }

    bool operator!=( const SmallArray<FIXED_SIZE,T>& src ) const
    {
        return not (*this == src);
    }

    bool operator<( const SmallArray<FIXED_SIZE,T>& src ) const
    {
        if ( mVarSize < src.mVarSize )
        {
            return true;
        }
        else  // same size
        {
            for ( uint i = 0; i < mVarSize; ++i )
            {
                if ( mArray[i] < src.mArray[i] )
                    return true;
                else if ( src.mArray[i] < mArray[i] )  // swapping operands substitutes greater than
                    return false;
                // continue, at this point, only know a subset is equal
            }
            return false;  // all elements equal
        }
    }

    // These are dangerous: overrunning SmallArray will corrupt call stack!
    T*             PTR( void ) const { return const_cast<T*>(mArray); }
    const T* CONST_PTR( void ) const { return mArray; }

    /// Limited STL compatibility.
    uint size( void ) const
    {
        return mVarSize;
    }

    void resize( const uint varSize )
    {
    ASSERT( varSize < FIXED_SIZE );

        mVarSize = varSize;
    }

    void clear( void )
    {
        mVarSize = 0;
    }

    bool empty( void ) const
    {
        return mVarSize == 0;
    }

    void push_back( T item )
    {
    ASSERT( mVarSize < FIXED_SIZE );

        mArray[mVarSize++] = item;
    }

private:
    uint    mVarSize;
    T       mArray[FIXED_SIZE];
};

template<uint FIXED_SIZE, typename T>
bool operator<( const SmallArray<FIXED_SIZE,T>& a1,
                const SmallArray<FIXED_SIZE,T>& a2 )
{
    return a1.operator<( a2 );  // phooey
}

} // namespace base

#endif // BASE_ARRAY_SMALL_HH
