/** 
 * @file
 * @author (C) 2006 Vassilii Khachaturov <vassilii@tarunz.org>, under GPLv2
 * @brief Input iterator wrapper functionality around a generator functor.
 * @version $Id: generator-iter.hpp,v 1.7 2006/06/05 07:50:18 vassilii Exp $
 */

template < typename Generator > struct generator_iterator_comp;
template < 
	typename Generator, 
	typename IterComp=generator_iterator_comp<Generator> 
> 
struct generator_input_iterator;

template < typename Generator > 
bool operator==(
		const generator_input_iterator<Generator>& x,
		const generator_input_iterator<Generator>& y);
template < typename Generator > 
bool operator!=(
		const generator_input_iterator<Generator>& x,
		const generator_input_iterator<Generator>& y);

/*
 * We also need something that would enable creating a generator_input_iterator
 * -like thing over a given generator that would stop generating things after
 * some given # of values generated. When incremented past the last generated
 * value, the iterator should no longer be dereferencable.
 *
 * We also want the iterator to be comparable in such a way that
 * the past-end values over the same generator would yield true under compar.
 *
 * 2 ways:
 * ->1) create a wrapper generator that stores the counter in it
 * and use the generator_input_iterator. This requires
 * rewriting the current ==/!= and requiring the Generator to be comparable.
 * this means that a preserved begin() from the wrapped generator
 * becomes invalid once the wrapped generator counter has expired
 *   2) create a different class for the iterator.
 */

// @todo XXX concept checking todo: result_type defined, is copy constructible
/// input iterator wrapper for a given Generator functor
template < typename Generator, typename IterComp >
struct generator_input_iterator
: std::iterator<std::input_iterator_tag, typename Generator::result_type>
{
    typedef typename Generator::result_type value_type;
	typedef value_type* pointer;
	typedef value_type& reference;

	generator_input_iterator(Generator& g) 
		: gen(g), res(gen()), nondereferenceable(false), prefetched(true)
			{}
	generator_input_iterator(Generator& g, bool valid) 
		: gen(g), nondereferenceable(!valid) 
		{ if (valid) { prefetch(); } }
	
	const reference operator*() const { cond_prefetch(); return res; }
	reference operator*() { cond_prefetch(); return res; }
	const pointer operator->() const { cond_prefetch(); return &res; }
	pointer operator->() { cond_prefetch(); return &res; }

	// prefix
	generator_input_iterator&
	operator++()
	{
		prefetched = false;
		return *this;
	}

	// postfix
	generator_input_iterator
	operator++(int)
	{
		generator_input_iterator old(*this);
		prefetched = false;
		return old;
	}

	bool singular() const { return nondereferenceable; }

private:
	Generator& gen;
	value_type res;
	bool nondereferenceable;
	bool prefetched;
	void prefetch() { res = gen(); prefetched = true; }
	/// XXX throw if nondereferenceable?
	void cond_prefetch() { if (!prefetched) prefetch(); }

	static IterComp comp;

	friend struct generator_iterator_comp<Generator>;
	friend bool operator== <>(const generator_input_iterator<Generator>& x, const generator_input_iterator<Generator>& y);
	friend bool operator!= <>(const generator_input_iterator<Generator>& x, const generator_input_iterator<Generator>& y);
};

/** Comparing 2 generators for the purpose of the generator_input_iterator
 * By default, compares via the pointer identity.
 * Can be overridden with a per-generator specialization to compare
 * via smth else (such as the == for the generator).
 */
template < typename Generator >
struct generator_iterator_comp
	: std::binary_function<
		const generator_input_iterator<Generator>&, 
		const generator_input_iterator<Generator>&, 
		bool> 
{
	bool operator() (const generator_input_iterator<Generator>& x, const generator_input_iterator<Generator>& y) 
	{ 
		return x.singular() ? y.singular()
			: !y.singular() && &x.gen == &y.gen; 
	}
};


template < typename Generator > 
bool operator==(
		const generator_input_iterator<Generator>& x,
		const generator_input_iterator<Generator>& y)
{
	return generator_input_iterator<Generator>::comp(x, y);
}

template < typename Generator > 
bool operator!=(
		const generator_input_iterator<Generator>& x,
		const generator_input_iterator<Generator>& y)
{
	return !generator_input_iterator<Generator>::comp(x, y);
}

// XXX maybe make it generic by the countdown counter type?
template < typename Generator >
struct countdown_generator {
	typedef typename Generator::result_type result_type;
	typedef generator_input_iterator< countdown_generator<Generator> > iterator;

	struct expired {};
	countdown_generator(Generator& g, size_t n) 
		: gen(g), remains(n), 
		first(iterator(*this, n>0)), last(iterator(*this, false))
		{}
	result_type operator()() { 
		if (remains--) return gen(); 
		// Otherwise the countdown has expired!
		remains = 0;
		throw expired();
	}

	iterator begin() { return first; }
	iterator end() { return last; }

private:
	Generator& gen;
	size_t remains;
	iterator first, last;

	friend struct generator_iterator_comp< countdown_generator<Generator> >;
};

template < typename Generator >
struct generator_iterator_comp< countdown_generator<Generator> >
: std::binary_function<
	const generator_input_iterator< countdown_generator<Generator> >&, 
	const generator_input_iterator< countdown_generator<Generator> >&, 
	bool>
{
	bool operator() (
			const generator_input_iterator< countdown_generator<Generator> >& x, const generator_input_iterator< countdown_generator<Generator> >& y) 
	{
		return x.singular() || !x.gen.remains 
			?  y.singular() || !y.gen.remains : 
			!y.singular()
			&& x.gen.remains == y.gen.remains
			&& &x.gen == &y.gen;
	}
};
