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

#ifndef __FOREACH_HPP_
#define __FOREACH_HPP_

namespace std {

template <class Iterator1, class Iterator2>
class const_pair_foreach_iterator {
	Iterator1 current1;
	Iterator2 current2;

public:
	typedef Iterator1 iterator_type1;
	typedef typename iterator_traits<Iterator1>::iterator_category iterator_category1;
	typedef typename iterator_traits<Iterator1>::value_type        value_type1;
	typedef Iterator1 pointer1;
	typedef const value_type1& reference1;

	typedef Iterator2 iterator_type2;
	typedef typename iterator_traits<Iterator2>::iterator_category iterator_category2;
	typedef typename iterator_traits<Iterator2>::value_type        value_type2;
	typedef Iterator2 pointer2;
	typedef const value_type2& reference2;

	explicit const_pair_foreach_iterator() {
	}

	explicit const_pair_foreach_iterator (Iterator1 it1, Iterator2 it2) : current1(it1), current2(it2) {
	}

	const_pair_foreach_iterator (const const_pair_foreach_iterator<Iterator1, Iterator2>& it) : current1(it.current1), current2(it.current2) {
	}

	const_pair_foreach_iterator& operator= (const const_pair_foreach_iterator<Iterator1, Iterator2>& it) {
		current1 = it.current1;
		current2 = it.current2;
	}

	iterator_type1 base1() const {
		return current1;
	}

	iterator_type2 base2() const {
		return current2;
	}

	std::tuple<reference1, reference2> operator*() const {
		return std::tie(*current1, *current2);
	}

	const_pair_foreach_iterator& operator++() {
		++current1;
		++current2;
		return *this;
	}

	const_pair_foreach_iterator& operator--() {
		--current1;
		--current2;
		return *this;
	}

	const_pair_foreach_iterator operator++(int) {
		const_pair_foreach_iterator temp = *this;
		++current1;
		++current2;
		return temp;
	}

	const_pair_foreach_iterator operator--(int) {
		const_pair_foreach_iterator temp = *this;
		--current1;
		--current2;
		return temp;
	}

	bool operator== (const const_pair_foreach_iterator<Iterator1, Iterator2>& other) {
		return this->current1 == other.current1 && this->current2 == other.current2;
	}

	bool operator!= (const const_pair_foreach_iterator<Iterator1, Iterator2>& other) {
		return ! ( *this == other );
	}

};

template<class Iterator1, class Iterator2>
const_pair_foreach_iterator<Iterator1, Iterator2> make_pair_foreach_iterator (Iterator1 it1, Iterator2 it2) {
	return const_pair_foreach_iterator<Iterator1, Iterator2>(it1, it2);
}

template<class T, class R>
class const_pair_foreach {

	const T& first;
	const R& second;

public:
	const_pair_foreach(const T& first, const R& second) : first(first), second(second) {}

	const_pair_foreach_iterator<typename T::const_iterator, typename R::const_iterator> begin() const {
		return make_pair_foreach_iterator(first.begin(), second.begin());
	}

	const_pair_foreach_iterator<typename T::const_iterator, typename R::const_iterator> end() const {
		return make_pair_foreach_iterator(first.end(), second.end());
	}

};

template<class T, class R>
const_pair_foreach<T, R> make_pair_foreach(const T& first, const R& second) {
	return const_pair_foreach<T, R>(first, second);
}

} /* namespace std */

#endif /* __FOREACH_HPP_ */