/*****************************************************************************
 * $LastChangedDate: 2010-02-03 19:48:51 -0500 (Wed, 03 Feb 2010) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   TinyArray 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_TINY_HH
#define BASE_ARRAY_TINY_HH 1

#include "base/types.hh"

namespace base {

////////////////////////////////////////////////////////////////////////////////
/// @brief Light-weight fixed-length Array class with index-checking.
///
/// Meant for small arrays.  Auto-destroys itself.
/// TinyArray is smaller/faster than STL vector but its size is fixed.
/// If instantiated on a function, TinyArray will be stored on call stack.
/// The size of the array is part of TinyArray's type.
/// Tested by base/tests/basic_types.cc.
///
template<uint COUNT, typename T=int>
class TinyArray
{
public:
    typedef T Type;  // type of element

public:
    /// Template arg count implicitly defines the array's length.
    TinyArray( void )
    {
        // NOP
    }

    /// Construct all elements with the same value.
    explicit TinyArray( const T val )
    {
        Fill( val );
    }

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

    // Copy constructor from a C array.
    // This syntax, which would be convenient, the compiler won't accept: TinyArray<> = { };
    TinyArray( const T src[] )
    {
        for ( uint i = 0; i < COUNT; ++i )
            mArray[i] = src[i];         
    }

    /// @return Amount of elements.
    uint GetCount( void ) const
    {
        return COUNT;
    }

    /// Assign every element the same value.
    void Fill( T val )
    {
        for ( uint i = 0; i < COUNT; ++i )
            mArray[i] = val;
    }

    /// 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 TinyArray is const
    {
        ASSERT( i < COUNT );
        return mArray[i];
    }

    const T& operator[]( uint i ) const     // compiled if TinyArray is const
    {
        ASSERT( i < COUNT );
        return mArray[i];
    }

    /// Amazing that a C++ compiler will accept how this type is written.
    TinyArray<COUNT,T>& operator=( const TinyArray<COUNT,T>& src )
    {
        for ( uint i = 0; i < COUNT; ++i )
            mArray[i] = src.mArray[i];  // assign
        return *this;
    }

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

    bool operator!=( const TinyArray<COUNT,T>& src ) const
    {
        // Same as operator==() but retvals opposite.
        for ( uint i = 0; i < COUNT; ++i )
        {
            if ( mArray[i] != src.mArray[i] )
                return true; // different
        }
        return false; // all elements equal
    }

    bool operator<( const TinyArray<COUNT,T>& src ) const
    {
        for ( uint i = 0; i < COUNT; ++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 TinyArray will corrupt call stack!
    T*             PTR( void ) const { return const_cast<T*>(mArray); }
    const T* CONST_PTR( void ) const { return mArray; }

    /// Limited STL compatibility.  Obviously, resize() omitted.
    uint size( void ) const
    {
        return COUNT;
    }

    bool empty( void ) const
    {
        return false;  // always has COUNT elements
    }

private:
    T   mArray[COUNT];
};

template<uint COUNT, typename T>
bool operator<( const TinyArray<COUNT,T>& a1,
                const TinyArray<COUNT,T>& a2 )
{
    return a1.operator<( a2 );  // yuck
}

} // namespace base

#endif // BASE_ARRAY_TINY_HH
