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

#ifndef BASE_FUNCS_STL_HH
#define BASE_FUNCS_STL_HH 1

#include <list>
#include <vector>
#include <deque>
#include <set>
#include <map>
#include <algorithm>

namespace base {

/*******************************************************************************
 * A "for each" macro for a STL container.
 * Recommended since a common typo is "<" or "==" instead of the correct "!=".
 * Example:
 * typedef vector<Object> Objects;
 * FOR_EACH( Objects, mObjects )
 * {
 *     Object object = *iter;
 * }
 *******************************************************************************/
#define FOR_EACH( CONTAINER_TYPE, CONTAINER_VAR ) for ( CONTAINER_TYPE::iterator iter = CONTAINER_VAR.begin(); iter != CONTAINER_VAR.end(); ++iter )

/*******************************************************************************
 * Helper for correctly writing operator<() for STL container.
 * bool operator<( const C& a, const C& b )
 * {
 *     RETURN_LT_TRUE_GT_FALSE( a.x, b.x )
 *     RETURN_LT_TRUE_GT_FALSE( a.y, b.y )
 *     return false;  // equal
 * }
 * "Effective C++" Item 21 says that a comparison function
 * should return false for equal values or it will corrupt a set/map.
 *******************************************************************************/
#define RETURN_LT_TRUE_GT_FALSE( X, Y )         \
{{                                              \
    if ( (X) < (Y) ) return true;               \
    if ( (Y) < (X) ) return false;              \
}}

/*******************************************************************************
 * Get raw C array from STL vector.
 * &v[0] is a STL idiom.
 *******************************************************************************/
template<typename T>       T*       PTR(       std::vector<T>& v ) { return &v[0]; }
template<typename T> const T* CONST_PTR( const std::vector<T>& v ) { return &v[0]; } 

/*******************************************************************************
 * Expand a sequential STL container if necessary.
 * Useful before operator[] to ensure container can hold it.
 *******************************************************************************/
template<typename SEQCONT> void Expand( SEQCONT& seqcont, uint idx )
{
ASSERT2( idx < MAX_ELEMS );

    // Be careful, resize() can implode a container too.
    if ( UX( idx >= seqcont.size() ) )
        seqcont.resize( idx + 1 );  // one more than index
}

/*******************************************************************************
 * @return True if two containers have equal contents.
 *******************************************************************************/
template<typename CONTAINER> bool Compare( const CONTAINER& c1, const CONTAINER& c2 )
{
    // Check sizes before calling std::equal() else it could overrun.
    if ( c1.size() == c2.size() )
        return std::equal( c1.begin(), c1.end(), c2.begin() );
    else
        return false;  // different sizes means not equal
}

/*****************************************************************************
 * @return True if container contains an item.
 *****************************************************************************/
template<typename KEY> bool Find( const std::list<KEY>& container, const KEY& key )  // STL list
{
    return std::find( container.begin(), container.end(), key ) != container.end();
}

template<typename KEY> bool Find( const std::vector<KEY>& container, const KEY& key )  // STL vector
{
    return std::find( container.begin(), container.end(), key ) != container.end();
}

template<typename KEY> bool Find( const std::deque<KEY>& container, const KEY& key )  // STL deque
{
    return std::find( container.begin(), container.end(), key ) != container.end();
}

template<typename KEY> bool Find( const std::set<KEY>& container, const KEY& key )  // STL set
{
    return container.find(key) != container.end();
}

template<typename KEY,typename VAL> bool Find( const std::map<KEY,VAL>& container, const KEY& key )  // STL map
{
    return container.find(key) != container.end();
}

/*******************************************************************************
 * Remove() is a compatible way to remove items from various STL containers.
 * Does NOT destroy/delete items.
 *******************************************************************************/
template<typename T> void Remove( std::vector<T>& container, const T& item )  // STL vector
{
    // erase/remove idiom for STL vectors or deques.  erase() returns nothing.
    container.erase( std::remove( container.begin(), container.begin(), item ),
                     container.end() );
}

template<typename T> void Remove( std::deque<T>& container, const T& item )  // STL deque
{
    container.erase( std::remove( container.begin(), container.begin(), item ),
                     container.end() );
}

template<typename T> void Remove( std::list<T>& container, const T& item )  // STL list
{
    container.remove( item );
}

template<typename KEY> void Remove( std::set<KEY>& container, const KEY& item )  // STL set
{
    container.erase( item );
}

template<typename KEY,typename VAL> void Remove( std::map<KEY,VAL>& container, const VAL& item )  // STL map
{
    container.erase( item );
}

/*****************************************************************************
 * @return True if two STL containers have duplicates.
 *****************************************************************************/
template<typename CONTAINER> bool IfDuplicate( const CONTAINER& c1, const CONTAINER& c2 )
{
    for ( typename CONTAINER::const_iterator i1 = c1.begin(); i1 != c1.end(); ++i1 )
    {
        if ( std::find( c2.begin(), c2.end(), *i1 ) != c2.end() )
            return true;
    }
    return false;
}

} // namespace base

#endif // BASE_FUNCS_STL_HH
