/* streamiterator.vala
 *
 * Copyright (C) 2013  Maciej Piechotka
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.

 * This library 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
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 *
 * Author:
 * 	Maciej Piechotka <uzytkownik2@gmail.com>
 */

internal class Gee.StreamIterator<A, G> : GLib.Object, Traversable<A>, Iterator<A> {
	public StreamIterator (Iterator<G> outer, owned StreamFunc<A, G> func) {
		_outer = outer;
		_func = (owned)func;
		_current = null;
		_need_next = true;
		_finished = false;

		_state = _func (Traversable.Stream.YIELD, null, out _current);
		switch (_state) {
		case Traversable.Stream.WAIT:
		case Traversable.Stream.YIELD:
			_need_next = !_outer.valid;
			break;
		case Traversable.Stream.CONTINUE:
			if (_outer.valid) {
				_state = _func (_state, new Lazy<G> (() => {
					return _outer.get ();
				}), out _current);
				switch (_state) {
				case Traversable.Stream.YIELD:
				case Traversable.Stream.CONTINUE:
				case Traversable.Stream.WAIT:
					break;
				case Traversable.Stream.END:
					_finished = true;
					return;
				default:
					assert_not_reached ();
				}
			}
			break;
		case Traversable.Stream.END:
			_finished = true;
			return;
		}
	}

	public bool foreach (ForallFunc<A> f) {
		Lazy<G>? current = null;
		if (_current != null) {
			if (!f (_current.value)) {
				return false;
			}
		}
		if (_next != null) {
			current = (owned)_next;
			if (!f (current.value)) {
				return false;
			}
		} else if (_finished) {
			return true;
		}
		unowned Iterator<G> outer = _outer;
		unowned StreamFunc<A, G> func = _func;
		Traversable.Stream state = _state;
		bool need_next = _need_next;
		bool result = true;
		Lazy<A>? next_current;
		Lazy<G>? outer_value = _outer_value;
		while ((next_current = yield_next<A, G>(outer, func, ref state, ref need_next, ref outer_value)) != null) {
			current = (owned)next_current;
			if (!f (current.value)) {
				result = false;
				break;
			}
		}
		_state = state;
		_need_next = need_next;
		_finished = result;
		_current = (owned)current;
		_outer_value = (owned)outer_value;
		return result;
	}

	public bool next () {
		if (has_next ()) {
			if (_current != null) {
				_current.eval ();
			}
			_current = (owned)_next;
			return true;
		} else {
			return false;
		}
	}

	public bool has_next () {
		if (_finished) {
			return false;
		}
		if (_next != null) {
			return true;
		}
		_next = yield_next<A, G> (_outer, _func, ref _state, ref _need_next, ref _outer_value);
		_finished = _next == null;
		return !_finished;
	}

	public new A get () {
		assert (_current != null);
		return _current.value;
	}

	public void remove () {
		assert_not_reached ();
	}

	public bool valid { get { return _current != null; } }
	public bool read_only { get { return true; } }

	private static inline Lazy<A>? yield_next<A, G> (Iterator<G> outer, StreamFunc<A, G> func, ref Traversable.Stream state, ref bool need_next, ref Lazy<G>? outer_value) {
		Lazy<A>? value = null;
		if (state != Traversable.Stream.CONTINUE)
			state = func (state, null, out value);
		while (true) {
			switch (state) {
			case Traversable.Stream.YIELD:
				return value;
			case Traversable.Stream.CONTINUE:
				if (outer_value != null) {
					outer_value.eval ();
				}
				if (need_next) {
					if (!outer.has_next ()) {
						state = func (Traversable.Stream.END, null, out value);
						continue;
					}
					outer_value = new Lazy<G> (() => {
						assert (outer.next ());
						return outer.get ();
					});
				} else {
					need_next = true;
					outer_value = new Lazy<G> (() => {
						return outer.get ();
					});
				}
				state = func (state, outer_value, out value);
				break;
			case Traversable.Stream.WAIT:
				state = func (state, null, out value);
				break;
			case Traversable.Stream.END:
				return null;
			default:
				assert_not_reached ();
			}
		}
	}

	private Iterator<G> _outer;
	private StreamFunc<A, G> _func;
	private Lazy<G>? _outer_value;
	private Lazy<A>? _current;
	private Lazy<A>? _next;
	private Traversable.Stream _state;
	private bool _need_next;
	private bool _finished;
}

