/*
 * foreach.hpp
 *
 * This file is part of Algorithms library toolkit.
 * Copyright (C) 2017 Jan Travnicek (jan.travnicek@fit.cvut.cz)

 * Algorithms library toolkit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * Algorithms library toolkit is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with Algorithms library toolkit.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Created on: Apr 1, 2013
 * Author: Jan Travnicek
 */

#ifndef __FOREACH_HPP_
#define __FOREACH_HPP_

#include <tuple>
#include <utility>

#include <extensions/container/tuple.hpp>

namespace ext {

/**
 * \brief
 * A class for packing a tuple of iterators and synchronizing them. All are incremented, decremented, dereferenced together.
 */
template < class ... Iterators >
class const_tuple_foreach_iterator {
	/**
	 * The underlying pack of iterators.
	 */
	ext::tuple < Iterators ... > current;

	/**
	 * \brief
	 * Helper method to dereference all operators. The method collects the results of dereference operator on individual iterators.
	 *
	 * \tparam I ... indexes to the the iterator tuple
	 *
	 * \return a tuple of values the individual iterators refer to
	 */
	template < size_t ... I >
	ext::tuple < const typename Iterators::value_type & ... > callDerefOperator ( const std::index_sequence < I ... > & ) const {
		return ext::tie ( * std::get < I > ( current ) ... );
	}

	/**
	 * \brief
	 * Helper method to increment all underlying iterators.
	 */
	template < size_t ... I >
	void callIncOperator ( const std::index_sequence < I ... > & ) {
		ext::tie ( ++std::get < I > ( current ) ... );
	}

	/**
	 * \brief
	 * Helper method to decrement all underlying iterators.
	 */
	template < size_t ... I >
	void callDecOperator ( const std::index_sequence < I ... > & ) {
		ext::tie ( --std::get < I > ( current ) ... );
	}

public:
	/**
	 * \brief
	 * Constructor of the iterator tuple from a pack of iterators.
	 *
	 * \param it ... pack of iterators
	 *
	 */
	explicit const_tuple_foreach_iterator ( Iterators ... it ) : current ( it ... ) {
	}

	/**
	 * \brief
	 * Getter of an Ith iterator from the tuple of underlying iterators.
	 *
	 * \tparam I the specification of the iterator index.
	 */
	template < size_t I >
	decltype ( std::get < I > ( current ) ) base ( ) const {
		return std::get < I > ( current );
	}

	/**
	 * \brief
	 * Implementation of dereference operator on all iterators in the tuple.
	 *
	 * \return a tuple of values the individual iterators refer to
	 */
	template < size_t ... I >
	ext::tuple < const typename Iterators::value_type & ... > operator *( ) const {
		return callDerefOperator ( std::index_sequence_for < Iterators ... > { } );
	}

	/**
	 * \brief
	 * Prefix version of increment operator that propagates the call to all underlying iterators.
	 *
	 * \return this
	 */
	const_tuple_foreach_iterator & operator ++( ) {
		callIncOperator ( std::index_sequence_for < Iterators ... > { } );
		return * this;
	}

	/**
	 * \brief
	 * Prefix version of decrement operator that propagates the call to all underlying iterators.
	 *
	 * \return this
	 */
	const_tuple_foreach_iterator & operator --( ) {
		callDecOperator ( std::index_sequence_for < Iterators ... > { } );
		return * this;
	}

	/**
	 * \brief
	 * Postfix version of increment operator that propagates the call to all underlying iterators.
	 *
	 * \return original value
	 */
	const_tuple_foreach_iterator operator ++( int ) {
		const_tuple_foreach_iterator temp = * this;

		++( * this );
		return temp;
	}

	/**
	 * \brief
	 * Postfix version of decrement operator that propagates the call to all underlying iterators.
	 *
	 * \return original value
	 */
	const_tuple_foreach_iterator operator --( int ) {
		const_tuple_foreach_iterator temp = * this;

		--( * this );
		return temp;
	}

	/**
	 * \brief
	 * Implementation of equality comparison operator on tuple of iterators
	 *
	 * \param other the other instance to compare with
	 *
	 * \return bool true if iterators of this class are equal to iterators of the other class
	 */
	bool operator ==( const const_tuple_foreach_iterator < Iterators ... > & other ) {
		return this->current == other.current;
	}

	/**
	 * \brief
	 * Implementation of inequality comparison operator on tuple of iterators
	 *
	 * \param other the other instance to compare with
	 *
	 * \return bool true if iterators of this class are not equal to iterators of the other class
	 */
	bool operator !=( const const_tuple_foreach_iterator < Iterators ... > & other ) {
		return !( * this == other );
	}

};

/**
 * \brief
 * Function construction of the tuple iterator.
 *
 * \tparam Iterators ... the types of interators to pack
 *
 * \param its ... the actual iterators to pack
 *
 * \return the tuple of iterators itself mimicking the iterator interface to some level
 */
template < class ... Iterators >
const_tuple_foreach_iterator < Iterators ... > make_tuple_foreach_iterator ( Iterators ... its ) {
	return const_tuple_foreach_iterator < Iterators ... > ( its ... );
}

/**
 * \brief
 * Class providing begin and end methods to allow simple use of packed iterators in foreach variant of the for loop
 *
 * \tparam Types ... the types of begin/end source classes
 */
template < class ... Types >
class const_tuple_foreach {

	/**
	 * \brief
	 * Source classes to retrieve begin and end iterators from
	 */
	const ext::tuple < const Types & ... > data;

	/**
	 * \brief
	 * Helper to call begin on source classes.
	 *
	 * \tparam I the specification of the iterator index
	 *
	 * \return the iterator tuple class
	 */
	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 ( ) ... );
	}

	/**
	 * \brief
	 * Helper to call end on source classes.
	 *
	 * \tparam I the specification of the iterator index
	 *
	 * \return the iterator tuple class
	 */
	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:
	/**
	 * \brief
	 * Constructor of foreach tuple pack helper
	 *
	 * \param args ... the actual begin/end source classes
	 */
	const_tuple_foreach ( const Types & ... args ) : data ( args ... ) { }

	/**
	 * \brief
	 * Getter of pack of begin iterators.
	 *
	 * \return the iterator tuple class
	 */
	const_tuple_foreach_iterator < typename Types::const_iterator ... > begin ( ) const {
		return callBegin ( std::index_sequence_for < Types ... > { } );
	}

	/**
	 * \brief
	 * Getter of pack of end iterators.
	 *
	 * \return the iterator tuple class
	 */
	const_tuple_foreach_iterator < typename Types::const_iterator ... > end ( ) const {
		return callEnd ( std::index_sequence_for < Types ... > { } );
	}

};

/**
 * \brief
 * Function construction of foreach tuple pack helper
 *
 * \tparam Types ... the types of begin/end source classes
 *
 * \param args ... the actual begin/end source classes
 *
 * \return the tuple of iterators itself mimicking the iterator interface to some level
 */
template < class ... Types >
const_tuple_foreach < Types ... > make_tuple_foreach ( const Types & ... args ) {
	return const_tuple_foreach < Types ... > ( args ... );
}

/**
 * \brief
 * Class wrapping an integral type, but with pointer iterface.
 *
 * \tparam IntegralType the type of values the iterator provides
 */
template < class IntegralType >
class virtual_pointer_to_integer {
	/**
	 * \brief
	 * The value of integral type.
	 */
	IntegralType m_data;

public:
	typedef IntegralType value_type;

	typedef std::ptrdiff_t difference_type;

	typedef IntegralType * pointer;

	typedef IntegralType reference;

	typedef std::bidirectional_iterator_tag iterator_category;

	/**
	 * \brief
	 * Constructor of the virtual pointer class.
	 *
	 * \param data the value to hold
	 */
	virtual_pointer_to_integer ( IntegralType data ) : m_data ( data ) {
	}

	/**
	 * \brief
	 * Method to retrieve the integral value.
	 *
	 * \return the current value
	 */
	IntegralType operator * ( ) const {
		return m_data;
	}

	/**
	 * \brief
	 * Prefix increment operator to increment the hold value.
	 *
	 * \return this
	 */
	virtual_pointer_to_integer & operator ++( ) {
		m_data ++;
		return * this;
	}

	/**
	 * \brief
	 * Prefix decrement operator to decrement the hold value.
	 *
	 * \return this
	 */
	virtual_pointer_to_integer & operator --( ) {
		m_data --;
		return * this;
	}

	/**
	 * \brief
	 * Postfix increment operator to increment the hold value.
	 *
	 * \return original value
	 */
	virtual_pointer_to_integer operator ++( int ) {
		virtual_pointer_to_integer temp = * this;

		++( * this );
		return temp;
	}

	/**
	 * \brief
	 * Postfix decrement operator to decrement the hold value.
	 *
	 * \return original value
	 */
	virtual_pointer_to_integer operator --( int ) {
		virtual_pointer_to_integer temp = * this;

		--( * this );
		return temp;
	}

	/**
	 * \brief
	 * Equality operator. Two classes are equal if the data they hold is the same.
	 *
	 * \param other the other instance to compare with
	 *
	 * \return bool true if this and other classes hold the same value
	 */
	bool operator ==( const virtual_pointer_to_integer < IntegralType > & other ) {
		return this->m_data == other.m_data;
	}

	/**
	 * \brief
	 * Inequality operator. Two classes are not equal if the data they hold is different.
	 *
	 * \param other the other instance to compare with
	 *
	 * \return bool true if this and other classes hold different value
	 */
	bool operator !=( const virtual_pointer_to_integer < IntegralType > & other ) {
		return !( * this == other );
	}
};

/**
 * \brief
 * Representation of integer sequence usable in foreach form of for loop.
 *
 * The class provides begin and end iterators.
 */
template < class IntegralType >
class sequence {
	/**
	 * \brief
	 * The start value of the sequence. Included in the sequence.
	 */
	IntegralType m_first;

	/**
	 * \brief
	 * The end value of the sequence. Not included in the sequence.
	 */
	IntegralType m_last;

public:
	/**
	 * \brief
	 * The constructor of the sequence based on the range
	 *
	 * \param first the start value of the sequence
	 * \param last the end value of the sequence
	 */
	sequence ( IntegralType first, IntegralType last ) : m_first ( first ), m_last ( last ) {
	}

	/**
	 * \brief
	 * Constructor of the sequence class based on the size.
	 *
	 * \param size the length of the sequence from zero
	 */
	sequence ( IntegralType size ) : m_first ( 0 ), m_last ( size ) {
	}

	/**
	 * \brief
	 * Getter of the begin iterator into the sequence.
	 *
	 * \return iterator to the begining of the sequence
	 */
	virtual_pointer_to_integer < IntegralType > begin ( ) {
		return virtual_pointer_to_integer < IntegralType > ( m_first );
	}

	/**
	 * \brief
	 * Getter of the end iterator into the sequence.
	 *
	 * \return iterator to the end of the sequence
	 */
	virtual_pointer_to_integer < IntegralType > end ( ) {
		return virtual_pointer_to_integer < IntegralType > ( m_last );
	}
};

} /* namespace ext */

#endif /* __FOREACH_HPP_ */
