/******************************************************************************/
/* ARRAYS *********************************************************************/
/******************************************************************************/

#ifndef _H_EBCL_ARRAYS
#define _H_EBCL_ARRAYS
#include <ebcl/Externals.hh>
#include <ebcl/Pointers.hh>
#include <ebcl/Utilities.hh>
#include <ebcl/Types.hh>
namespace ebcl {


// TODO:
//	* add addAll({})
//	* add ={} / cons({})


/*= DYNAMIC ARRAYS ===========================================================*/

template< typename Type >
class T_Array final
{
   public:
	using T_Self = T_Array< Type >;

	static constexpr uint32_t DEFAULT_GROWTH( )
	{
		return std::max< uint32_t >( 1 , 4096 / sizeof( Type ) );
	}

   private:
	Type* data_;
	uint32_t capacity_;
	uint32_t size_;
	uint32_t growth_;

   public:
	M_TEMPLATE_POINTERS( T_Self );

	T_Array( ) noexcept;
	explicit T_Array( uint32_t growth ) noexcept;

	// ---------------------------------------------------------------------

	T_Array( T_Self const& source ) noexcept;
	T_Self& operator= ( T_Self const& other ) noexcept;

	T_Array( T_Self&& source ) noexcept;
	T_Self& operator= ( T_Self&& other ) noexcept;

	T_Array( std::initializer_list< Type > list ) noexcept;
	T_Self& operator= ( std::initializer_list< Type > list ) noexcept;

	template< typename TP >
		friend void swap( T_Array< TP >& lhs ,
				T_Array< TP >& rhs ) noexcept;

	// ---------------------------------------------------------------------

	~T_Array( );

	T_Self& clear( ) noexcept;
	T_Self& free( ) noexcept;

	// ---------------------------------------------------------------------

	uint32_t capacity( ) const noexcept;
	uint32_t size( ) const noexcept;
	uint32_t growth( ) const noexcept;
	bool empty( ) const noexcept;

	T_Self& ensureCapacity( uint32_t capacity ) noexcept;

	template<
		typename Q = Type ,
		typename = std::enable_if_t< std::is_default_constructible< Q >::value >
	> T_Self& resize( const uint32_t size );

	template<
		typename Q = Type ,
		typename = std::enable_if_t< std::is_copy_constructible< Q >::value >
	> T_Self& resize( const uint32_t size ,
			Type const& value );

	// ---------------------------------------------------------------------

	Type& operator[] ( uint32_t index ) noexcept;
	Type const& operator[] ( uint32_t index ) const noexcept;

	Type& last( ) noexcept;
	Type const& last( ) const noexcept;

	// Find items (requires comparison operator)
	int32_t indexOf( Type const& item ) const noexcept;
	bool contains( Type const& item ) const noexcept;

	// Find items based on a predicate
	int32_t indexOf( std::function< bool( Type const& ) > pred ) const noexcept;
	bool contains( std::function< bool( Type const& ) > pred ) const noexcept;

	// ---------------------------------------------------------------------

	uint32_t add( Type const& item ) noexcept;
	uint32_t add( Type&& item ) noexcept;

	template< typename... Args >
		Type& addNew( Args&& ... args );

	// ---------------------------------------------------------------------

	T_Self& addAll( T_Self const& other ) noexcept;
	T_Self& addAll( T_Self&& other ) noexcept;
	T_Self& addAll( std::initializer_list< Type > values ) noexcept;

	// ---------------------------------------------------------------------

	T_Self& operator<< ( Type const& item ) noexcept;
	T_Self& operator<< ( Type&& item ) noexcept;
	T_Self& operator<< ( T_Self const& other ) noexcept;
	T_Self& operator<< ( T_Self&& other ) noexcept;

	// ---------------------------------------------------------------------

	void insert( uint32_t index ,
			Type const& item ) noexcept;
	void insert( uint32_t index ,
			Type&& item ) noexcept;

	template< typename... Args >
		Type& insertNew( uint32_t index ,
				Args&& ... args );

	// ---------------------------------------------------------------------

	void remove( uint32_t index ) noexcept;
	void removeSwap( uint32_t index ) noexcept;
	void removeLast( ) noexcept;

	// ---------------------------------------------------------------------

	void sort( F_Comparator< Type > cmp = T_Comparator< Type >::compare ) noexcept;
	void sort( uint32_t first ,
			uint32_t items ,
			F_Comparator< Type > cmp = T_Comparator< Type >::compare ) noexcept;

	// ---------------------------------------------------------------------

	T_Self copyRange( uint32_t first ,
			uint32_t last = UINT32_MAX ) const noexcept;

	T_Self moveRange( uint32_t first ,
			uint32_t last = UINT32_MAX ) noexcept;

	// ---------------------------------------------------------------------
	// C++ iterators
#	include "ebcl/bits/ArrayIteratorDecl.hh"
#	include "ebcl/bits/ArrayConstIteratorDecl.hh"
#	include "ebcl/bits/IteratorMethodsDecl.hh"
};

// Instantiate some common types directly
extern template class T_Array< uint32_t >;


/*= STATICALLY ALLOCATED ARRAYS ==============================================*/
/* These arrays offer the same interface as dynamic arrays, but are in fact
 * implemented as in-place storage.
 */

template< typename Type , uint32_t Size >
class T_StaticArray final
{
	static_assert( Size > 0 , "Size must be greater than 0" );

    public:
	using T_Self = T_StaticArray< Type , Size >;

    private:
	// Actual storage type
	using T_Storage_ = std::aligned_storage_t<
		sizeof( Type ) , alignof( Type )
	>;

	T_Storage_ storage_[ Size ];
	uint32_t size_;

   public:
	M_TEMPLATE_POINTERS( T_Self );

	T_StaticArray( ) noexcept;

	// Copy
	T_StaticArray( T_Self const& source ) noexcept;
	T_Self& operator= ( T_Self const& other ) noexcept;

	// Move
	T_StaticArray( T_Self&& source ) noexcept;
	T_Self& operator= ( T_Self&& other ) noexcept;

	~T_StaticArray( ) noexcept;
	T_Self& clear( ) noexcept;

	template< typename T , uint32_t S >
	friend void swap(
			T_StaticArray< T , S >& lhs ,
			T_StaticArray< T , S >& rhs ) noexcept;

	// ---------------------------------------------------------------------

	constexpr uint32_t capacity( ) const noexcept;
	uint32_t size( ) const noexcept;
	bool empty( ) const noexcept;

	// ---------------------------------------------------------------------

	Type& operator[] ( uint32_t index ) noexcept;
	Type const& operator[] ( uint32_t index ) const noexcept;

	Type& last( ) noexcept;
	Type const& last( ) const noexcept;

	// Find items (requires comparison operator)
	int32_t indexOf( Type const& item ) const noexcept;
	bool contains( Type const& item ) const noexcept;

	// Find items based on a predicate
	int32_t indexOf( std::function< bool( Type const& ) > pred ) const noexcept;
	bool contains( std::function< bool( Type const& ) > pred ) const noexcept;

	// ---------------------------------------------------------------------

	uint32_t add( Type&& item ) noexcept;
	uint32_t add( Type const& item ) noexcept;
	template< typename... Args >
		Type& addNew( Args&& ... args );

	// ---------------------------------------------------------------------

	T_Self& addAll( T_Self const& other ) noexcept;
	T_Self& addAll( T_Self&& other ) noexcept;

	// ---------------------------------------------------------------------

	T_Self& operator<< ( Type const& item ) noexcept;
	T_Self& operator<< ( Type&& item ) noexcept;
	T_Self& operator<< ( T_Self const& other ) noexcept;
	T_Self& operator<< ( T_Self&& other ) noexcept;

	// ---------------------------------------------------------------------

	void insert( uint32_t index ,
			Type&& item ) noexcept;
	void insert( uint32_t index ,
			Type const& item ) noexcept;
	template< typename... Args >
		Type& insertNew( uint32_t index ,
				Args&& ... args );

	// ---------------------------------------------------------------------

	void remove( uint32_t index ) noexcept;
	void removeSwap( uint32_t index ) noexcept;
	void removeLast( ) noexcept;

	// ---------------------------------------------------------------------

	void sort( F_Comparator< Type > cmp = T_Comparator< Type >::compare ) noexcept;
	void sort( uint32_t first ,
			uint32_t items ,
			F_Comparator< Type > cmp = T_Comparator< Type >::compare ) noexcept;

	// ---------------------------------------------------------------------

	T_Self copyRange( uint32_t first ,
			uint32_t last = UINT32_MAX ) const noexcept;
	T_Self moveRange( uint32_t first ,
			uint32_t last = UINT32_MAX ) noexcept;

	// ---------------------------------------------------------------------
	// C++ iterators
#	include "ebcl/bits/ArrayIteratorDecl.hh"
#	include "ebcl/bits/ArrayConstIteratorDecl.hh"
#	include "ebcl/bits/IteratorMethodsDecl.hh"
};


/*= AUTOMATIC ARRAYS =========================================================*/
/* Arrays that are stored in-place up to some limit, then transform into
 * dynamic arrays if necessary.
 */

template<
	typename Type , uint32_t StaticSize ,
	uint32_t DynamicGrowth = StaticSize * 4
> class T_AutoArray
{
    public:
	using T_Self = T_AutoArray< Type , StaticSize , DynamicGrowth >;

    private:
	using T_Static_ = T_StaticArray< Type , StaticSize >;
	using T_Dynamic_ = T_Array< Type >;

	T_Union< T_Static_ , T_Dynamic_ > array_;

	// ---------------------------------------------------------------------

    public:
	M_TEMPLATE_POINTERS( T_Self );

	T_AutoArray( ) noexcept;

	T_AutoArray( T_Self const& source ) noexcept;
	T_Self& operator =( T_Self const& source ) noexcept;

	T_AutoArray( T_Self&& source ) noexcept;
	T_Self& operator =( T_Self&& source ) noexcept;

	// ---------------------------------------------------------------------

	template< typename TP , uint32_t S , uint32_t G >
		friend void swap(
				T_AutoArray< TP , S , G >& lhs ,
				T_AutoArray< TP , S , G >& rhs ) noexcept;

	// ---------------------------------------------------------------------

	T_Self& clear( ) noexcept;
	T_Self& free( ) noexcept;

	// ---------------------------------------------------------------------

	uint32_t capacity( ) const noexcept;
	uint32_t size( ) const noexcept;
	constexpr uint32_t growth( ) const noexcept;
	bool isStatic( ) const noexcept;
	bool empty( ) const noexcept;

	T_Self& ensureCapacity( uint32_t capacity ) noexcept;

	// ---------------------------------------------------------------------

	Type& operator[] ( uint32_t index ) noexcept;
	Type const& operator[] ( uint32_t index ) const noexcept;

	Type& last( ) noexcept;
	Type const& last( ) const noexcept;

	// Find items (requires comparison operator)
	int32_t indexOf( Type const& item ) const noexcept;
	bool contains( Type const& item ) const noexcept;

	// Find items based on a predicate
	int32_t indexOf( std::function< bool( Type const& ) > pred ) const noexcept;
	bool contains( std::function< bool( Type const& ) > pred ) const noexcept;

	// ---------------------------------------------------------------------

	uint32_t add( Type const& item ) noexcept;
	uint32_t add( Type&& item ) noexcept;

	template< typename... Args >
		Type& addNew( Args&& ... args );

	// ---------------------------------------------------------------------

	T_Self& addAll( T_Self const& other ) noexcept;
	T_Self& addAll( T_Self&& other ) noexcept;

	// ---------------------------------------------------------------------

	T_Self& operator<< ( Type const& item ) noexcept;
	T_Self& operator<< ( Type&& item ) noexcept;
	T_Self& operator<< ( T_Self const& other ) noexcept;
	T_Self& operator<< ( T_Self&& other ) noexcept;

	// ---------------------------------------------------------------------

	void insert( uint32_t index ,
			Type const& item ) noexcept;
	void insert( uint32_t index ,
			Type&& item ) noexcept;

	template< typename... Args >
		Type& insertNew( uint32_t index ,
				Args&& ... args );

	// ---------------------------------------------------------------------

	void remove( uint32_t index ) noexcept;
	void removeSwap( uint32_t index ) noexcept;
	void removeLast( ) noexcept;

	// ---------------------------------------------------------------------

	void sort( F_Comparator< Type > cmp = T_Comparator< Type >::compare ) noexcept;
	void sort( uint32_t first ,
			uint32_t items ,
			F_Comparator< Type > cmp = T_Comparator< Type >::compare ) noexcept;

	// ---------------------------------------------------------------------

	T_Self copyRange( uint32_t first ,
			uint32_t last = UINT32_MAX ) const noexcept;

	T_Self moveRange( uint32_t first ,
			uint32_t last = UINT32_MAX ) noexcept;

	// ---------------------------------------------------------------------
	// C++ iterators
#	include "ebcl/bits/ArrayIteratorDecl.hh"
#	include "ebcl/bits/ArrayConstIteratorDecl.hh"
#	include "ebcl/bits/IteratorMethodsDecl.hh"

	// ---------------------------------------------------------------------

    private:
	void convertToDynamic_( uint32_t capacity ) noexcept;

	T_Static_& static_( ) noexcept;
	T_Static_ const& static_( ) const noexcept;
	T_Dynamic_& dynamic_( ) noexcept;
	T_Dynamic_ const& dynamic_( ) const noexcept;
};


}
#endif // _H_EBCL_ARRAYS
#include <ebcl/inline/Arrays.hh>