/*
 * foreach.hpp
 *
 * Created on: Apr 1, 2013
 * Author: Jan Travnicek
 */

#ifndef __FOREACH_HPP_
#define __FOREACH_HPP_

namespace std {

template < class ... Iterators >
class const_tuple_foreach_iterator {
	std::tuple < Iterators ... > current;

	template < size_t ... I >
	std::tuple < const typename Iterators::value_type & ... > callDerefOperator ( const std::index_sequence < I ... > & ) const {
		return std::tie ( * std::get < I > ( current ) ... );
	}

	template < size_t ... I >
	void callIncOperator ( const std::index_sequence < I ... > & ) {
		std::tie ( ++std::get < I > ( current ) ... );
	}

	template < size_t ... I >
	void callDecOperator ( const std::index_sequence < I ... > & ) {
		std::tie ( --std::get < I > ( current ) ... );
	}

public:
	explicit const_tuple_foreach_iterator ( ) {
	}

	explicit const_tuple_foreach_iterator ( Iterators ... it ) : current ( it ... ) {
	}

	template < int number >
	decltype ( std::get < number > ( current ) )base ( ) const {
		return std::get < number > ( current );
	}

	template < size_t ... I >
	std::tuple < const typename Iterators::value_type & ... > operator *( ) const {
		return callDerefOperator ( std::index_sequence_for < Iterators ... > { } );
	}

	template < size_t ... I >
	const_tuple_foreach_iterator & operator ++( ) {
		callIncOperator ( std::index_sequence_for < Iterators ... > { } );
		return * this;
	}

	template < size_t ... I >
	const_tuple_foreach_iterator & operator --( ) {
		callDecOperator ( std::index_sequence_for < Iterators ... > { } );
		return * this;
	}

	const_tuple_foreach_iterator operator ++( int ) {
		const_tuple_foreach_iterator temp = * this;

		++( * this );
		return temp;
	}

	const_tuple_foreach_iterator operator --( int ) {
		const_tuple_foreach_iterator temp = * this;

		--( * this );
		return temp;
	}

	bool operator ==( const const_tuple_foreach_iterator < Iterators ... > & other ) {
		return this->current == other.current;
	}

	bool operator !=( const const_tuple_foreach_iterator < Iterators ... > & other ) {
		return !( * this == other );
	}

};

template < class ... Iterators >
const_tuple_foreach_iterator < Iterators ... > make_tuple_foreach_iterator ( Iterators ... its ) {
	return const_tuple_foreach_iterator < Iterators ... > ( its ... );
}

template < class ... Types >
class const_tuple_foreach {

	const std::tuple < const Types & ... > data;

	template < size_t ... I >
	const_tuple_foreach_iterator < typename Types::const_iterator ... > callBegin ( const std::index_sequence < I ... > & ) const {
		return make_tuple_foreach_iterator ( std::get < I > ( data ).cbegin ( ) ... );
	}

	template < size_t ... I >
	const_tuple_foreach_iterator < typename Types::const_iterator ... > callEnd ( const std::index_sequence < I ... > & ) const {
		return make_tuple_foreach_iterator ( std::get < I > ( data ).cend ( ) ... );
	}

public:
	const_tuple_foreach ( const Types & ... args ) : data ( args ... ) { }

	template < int ... I >
	const_tuple_foreach_iterator < typename Types::const_iterator ... > begin ( ) const {
		return callBegin ( std::index_sequence_for < Types ... > { } );
	}

	template < int ... I >
	const_tuple_foreach_iterator < typename Types::const_iterator ... > end ( ) const {
		return callEnd ( std::index_sequence_for < Types ... > { } );
	}

};

template < class ... Types >
const_tuple_foreach < Types ... > make_tuple_foreach ( const Types & ... args ) {
	return const_tuple_foreach < Types ... > ( args ... );
}

} /* namespace std */

#endif /* __FOREACH_HPP_ */