/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
***************************************************************************/

#pragma once

#include "glmPODArray.h"
#include "glmList.h"
#include "glmSet.h"
#include "glmStringOperators.h"

/// \name glm::Array operators
///@{
/**
* \brief Append a single element to a vector
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template <class T>
glm::Array<T>& 
	operator+=( glm::Array<T>& v, T const& s )
{
	v.push_back(s) ;
	return v ;
}
/**
* \brief Store elements of a set in a vector
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template <class T, class Cmp>
inline glm::Array<T>& 
	operator+=( glm::Array<T>& v, glm::GlmSet<T> const& s )
{
	v.resize( s.size() ) ;
	int cpt=0; 
	for (typename glm::GlmSet<T>::const_iterator it = s.begin(); it != s.end(); it++, cpt++)
	{
		v[cpt] = *it ;
	}
	return v ;
}
///@}



/// \name glm::GlmSet operators (concerning single elements)
///@{
/**
* \brief Check if an element exists in a set
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template <class T, class Cmp>
inline bool 
	in( T const& e, glm::GlmSet<T> const& s )
{
	return ( s.find(e) != s.end() ) ;
}
/**
* \brief Append a single element to a set
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template <class T, class Cmp>
inline glm::GlmSet<T>& 
	operator+=( glm::GlmSet<T>& s, T const& e )
{
	typename glm::GlmSet<T>::iterator pos = s.find( e ) ;
	if( pos == s.end() )
	{
		s.insert(e) ;
	}
	return s ;
}
/**
* \brief Remove an element from a set
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template <class T, class Cmp>
inline glm::GlmSet<T>& 
	operator-=( glm::GlmSet<T>& s, T const& e )
{
	typename glm::GlmSet<T>::iterator pos = s.find( e ) ;
	if( pos != s.end() )
	{
		s.erase( pos ) ;
	}
	return s ;
}
/**
* \brief X-OR an element to a set (remove if it exists in set, add otherwise)
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template<class T, class Cmp>
inline glm::GlmSet<T>& 
	operator^=( glm::GlmSet<T>& s, T const& e )
{
	if( in( e, s ) )
	{
		s -= e ;
	}
	else
	{
		s += e ;
	}
	return s ;
}
///@}



/// \name glm::GlmSet operators (concerning other containers, resulting current glm::GlmSet container)
///@{
/**
* \brief Store elements of a list in a set
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template <class T, class Cmp>
glm::GlmSet<T>& 
	operator+=( glm::GlmSet<T>& s, glm::GlmList<T> const& l )
{
	for( typename glm::GlmList<T>::const_iterator it = l.begin() ; it != l.end() ; it++ )
	{
		s += (*it) ;
	}
	return s ;
}
/**
* \brief Remove elements of a list existing in a set
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template <class T1, class T2, class Cmp>
glm::GlmSet<T1>& 
	operator-=( glm::GlmSet<T1>& s, glm::GlmList<T2> const& l )
{
	for( typename glm::GlmList<T2>::const_iterator it = l.begin() ; it != l.end() ; it++ )
	{
		s -= (*it) ;
	}
	return s ;
}
/**
* \brief Append elements of a vector to a set
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template <class T, class Cmp>
glm::GlmSet<T>& 
	operator+=( glm::GlmSet<T>& s, glm::Array<T> const& l )
{
	for( typename glm::Array<T>::const_iterator it = l.begin() ; it != l.end() ; it++ )
	{
		s += (*it) ;
	}
	return s ;
}
/**
* \brief Remove elements of a vector existing in a set
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template <class T1, class T2, class Cmp>
glm::GlmSet<T1>&
	operator-=( glm::GlmSet<T1>& s, glm::Array<T2> const& l )
{
	for( typename glm::Array<T2>::const_iterator it = l.begin() ; it != l.end() ; it++ )
	{
		s-=(*it) ;
	}
	return s ;
}
/**
* \brief Append elements of a set in the current set (set union)
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template <class T, class Cmp>
glm::GlmSet<T>& 
	operator+=( glm::GlmSet<T>& s1, glm::GlmSet<T> const& s2 )
{
	for( typename glm::GlmSet<T>::const_iterator it = s2.begin() ; it != s2.end() ; it++ )
	{
		s1 += *it ;
	}
	return s1 ;
}
/**
* \brief Remove elements of a set existing in the current set (set substraction)
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template <class T, class Cmp>
glm::GlmSet<T>& 
	operator-=( glm::GlmSet<T>& s1, glm::GlmSet<T> const& s2)
{
	for( typename glm::GlmSet<T>::const_iterator it = s2.begin() ; it != s2.end() ; it++ )
	{
		s1 -= *it ;
	}
	return s1 ;
}
/**
* \brief Remove elements of the current set not existing in a given set (set intersection)
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template <class T, class Cmp>
glm::GlmSet<T>& 
	operator*=( glm::GlmSet<T>& s1, glm::GlmSet<T> const& s2)
{
	for( typename glm::GlmSet<T>::iterator it = s1.begin() ; it != s1.end() ; it++ )
	{
		if( !in( *it, s2 ) )
		{
			s1.erase( it ) ;
		}
	}
	return s1 ; 
}
///@}



/// \name glm::GlmSet operators (concerning glm::GlmSet containers, resulting new glm::GlmSet containers)
///@{
/**
* \brief Union of two sets
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template <class T, class Cmp>
inline glm::GlmSet<T>
	operator+( glm::GlmSet<T> const& s1, glm::GlmSet<T> const& s2 )
{
	if( s1.size() < s2.size() )
	{
		return s2 + s1 ;
	}
	glm::GlmSet<T> result( s1 ) ;
	for( typename glm::GlmSet<T>::const_iterator it = s2.begin() ; it != s2.end() ; it++ )
	{
		if( s1.find( *it ) == s1.end() )
		{
			result.insert( *it ) ;
		}
	}
	return result ;
}
/**
* \brief Substraction of two sets
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template <class T, class Cmp>
inline glm::GlmSet<T> 
	operator-( glm::GlmSet<T> const& s1, glm::GlmSet<T> const& s2 )
{
	glm::GlmSet<T> result( s1 ) ;
	for( typename glm::GlmSet<T>::const_iterator it = s2.begin() ; it != s2.end() ; it++ )
	{
		if( s1.find( *it ) != s1.end() )
		{
			result.erase( *it ) ;
		}
	}
	return result ;
}
/**
* \brief Intersection of two sets
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template <class T, class Cmp>
inline glm::GlmSet<T> 
	operator*( glm::GlmSet<T> const& s1, glm::GlmSet<T> const& s2 )
{
	glm::GlmSet<T> result ;
	if( s2.size() < s1.size() )
	{
		return s2*s1 ;
	}
	for( typename glm::GlmSet<T>::const_iterator it = s1.begin() ; it != s1.end() ; it++ )
	{
		if( s2.find( *it) != s2.end() )
		{
			result.insert( *it ) ;
		}
	}
	return result ;
}
/**
* \brief Difference (XOR) between two sets
* \author Golaem <dev@golaem.com>
* \ingroup containers
*/
template <class T, class Cmp>
inline glm::GlmSet<T> 
	operator^( glm::GlmSet<T> const& s1, glm::GlmSet<T> const& s2 )
{
	if( s2.size() < s1.size() )
	{
		return s2^s1 ;
	}
	glm::GlmSet<T> result( s2 ) ;
	for( typename glm::GlmSet<T>::const_iterator it = s1.begin() ; it != s1.end() ; it++ )
	{
		if( s2.find( *it ) == s2.end() )
		{
			result.insert( *it ) ;
		}
		else
		{
			result.erase( *it ) ;
		}
	}
	return result ;
}
/**
* \brief Outputs a given set in a stream.
*
* \author Golaem <dev@golaem.com>
* \ingroup containers
* \tparam T The data type of the set items.
* \tparam Cmp The compare functor used to order the items.
*/
template <class T, class Cmp>
std::ostream & operator<<(std::ostream & out, const glm::GlmSet<T> & set)
{
	out<<"{ " ;
	for(typename glm::GlmSet<T>::const_iterator it=set.begin() ; it!=set.end() ; it++)
	{
		out << glm::toString<T>(*it) << " " ;
	}
	out<<"}" ;
	return out ;
}
///@}



/// \name Generic set operators
///@{
namespace glm
{
	/**
	* \brief Check if a given value exists in a set.
	*
	* \author Golaem <dev@golaem.com>
	* \ingroup containers
	* \tparam T A type.
	* \tparam SetOfT A set of T type.
	*/
	template <class T, class SetOfT>
	inline bool setIsContaining(const T & value, const SetOfT & set)
	{
		return (set.find(value)!=set.end()) ;
	}
}

