/**********************************************************************************

 Infomap software package for multi-level network clustering

 Copyright (c) 2013, 2014 Daniel Edler, Martin Rosvall
 
 For more information, see <http://www.mapequation.org>
 

 This file is part of Infomap software package.

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

 Infomap software package 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 Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with Infomap software package.  If not, see <http://www.gnu.org/licenses/>.

**********************************************************************************/


#ifndef GAP_ITERATOR_H_
#define GAP_ITERATOR_H_

#include <iterator>

namespace infomap {

//using std::iterator; // Can inherit from std::iterator to automatically forward typedefs
using std::iterator_traits;

template<typename _Iterator>
class gap_iterator
{
public:
	typedef _Iterator												iterator_type;
	typedef typename iterator_traits<_Iterator>::iterator_category	iterator_category;
	typedef typename iterator_traits<_Iterator>::value_type  		value_type;
	typedef typename iterator_traits<_Iterator>::difference_type	difference_type;
	typedef typename iterator_traits<_Iterator>::reference 			reference;
	typedef typename iterator_traits<_Iterator>::pointer   			pointer;

	gap_iterator(iterator_type first,
			iterator_type firstEnd,
			iterator_type second) :
				m_first(first),
				m_firstEnd(firstEnd),
				m_second(second),
				m_isFirst(m_first != m_firstEnd),
				m_current(m_isFirst ? m_first : m_second)
	{ }

	gap_iterator(iterator_type secondEnd) :
		m_first(secondEnd),
		m_firstEnd(secondEnd),
		m_second(secondEnd),
		m_isFirst(false),
		m_current(secondEnd)
	{ }

	/**
	 *  Copy constructor.
	 */
	gap_iterator(const gap_iterator& other) :
		m_first(other.m_first),
		m_firstEnd(other.m_firstEnd),
		m_second(other.m_second),
		m_isFirst(other.m_isFirst),
		m_current(other.m_current)
	{ }

	reference
	operator*() const
	{ return *m_current; }

	pointer
	operator->() const
	{ return m_current.operator->(); }

	gap_iterator&
	operator++()
	{
		++m_current;
		if (m_current == m_firstEnd)
		{
			m_current = m_second;
			m_isFirst = false;
		}
		return *this;
	}

	gap_iterator&
	operator--()
	{
		if (m_current == m_second)
		{
			m_current = m_firstEnd;
			m_isFirst = true;
		}
		--m_current;
		return *this;
	}

	const _Iterator&
	base() const
	{ return m_current; }

	bool isFirst() const
	{ return m_isFirst; }

private:
	iterator_type m_first;
	iterator_type m_firstEnd;
	iterator_type m_second;
	bool m_isFirst;
	_Iterator m_current;
};

// copy from stl_iterator.h
/**
 * Notice:
 * The compare functions need to check that it compares elements from the same
 * iterator, because on the STL iterator, the dummy end element can be equal to
 * a real element on another iterator. It actually happened that the end element
 * on the second iterator was equal to the begin element on the first even though
 * both iterator had length 2, thus this iterator behaved as there was no elements
 * to iterate over!
 */
template<typename _Iterator>
inline bool
operator==(const gap_iterator<_Iterator>& __x,
		const gap_iterator<_Iterator>& __y)
{ return !(__x.isFirst() ^ __y.isFirst()) && (__x.base() == __y.base()); }

template<typename _Iterator>
inline bool
operator<(const gap_iterator<_Iterator>& __x,
		const gap_iterator<_Iterator>& __y)
{ return (__x.isFirst() && !__y.isFirst()) || (__y.base() < __x.base()); }

template<typename _Iterator>
inline bool
operator!=(const gap_iterator<_Iterator>& __x,
		const gap_iterator<_Iterator>& __y)
{ return !(__x == __y); }

template<typename _Iterator>
inline bool
operator>(const gap_iterator<_Iterator>& __x,
		const gap_iterator<_Iterator>& __y)
{ return __y < __x; }

template<typename _Iterator>
inline bool
operator<=(const gap_iterator<_Iterator>& __x,
		const gap_iterator<_Iterator>& __y)
{ return !(__y < __x); }

template<typename _Iterator>
inline bool
operator>=(const gap_iterator<_Iterator>& __x,
		const gap_iterator<_Iterator>& __y)
{ return !(__x < __y); }

}

#endif /* GAP_ITERATOR_H_ */
