// This software is released into the Public Domain.  See copying.txt for details.
package org.openstreetmap.osmosis.apidb.v0_6.impl;

import java.util.ArrayList;
import java.util.List;

import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
import org.openstreetmap.osmosis.core.store.PeekableIterator;


/**
 * Reads a history stream and groups all changes for a single entity into lists.
 */
public class EntityHistoryListReader implements ReleasableIterator<List<ChangeContainer>> {
	private PeekableIterator<ChangeContainer> sourceIterator;


	/**
	 * Creates a new instance.
	 * 
	 * @param sourceIterator
	 *            An iterator containing full entity history ordered by type, identifier and
	 *            version.
	 */
	public EntityHistoryListReader(ReleasableIterator<ChangeContainer> sourceIterator) {
		this.sourceIterator = new PeekableIterator<ChangeContainer>(sourceIterator);
	}


	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean hasNext() {
		return sourceIterator.hasNext();
	}


	/**
	 * {@inheritDoc}
	 */
	@Override
	public List<ChangeContainer> next() {
		List<ChangeContainer> changeList;
		Entity peekEntity;
		long currentId;
		EntityType currentEntityType;
		
		// Get the next change from the underlying stream.
		peekEntity = sourceIterator.peekNext().getEntityContainer().getEntity();
		currentId = peekEntity.getId();
		currentEntityType = peekEntity.getType();
		
		// Loop until all history values for the current element are exhausted.
		changeList = new ArrayList<ChangeContainer>();
		while (sourceIterator.hasNext()) {
			ChangeContainer tmpChangeContainer = sourceIterator.peekNext();
			
			// Break out of the loop when we reach the next entity in the stream.
			if (currentId != tmpChangeContainer.getEntityContainer().getEntity().getId()
				|| !currentEntityType.equals(tmpChangeContainer.getEntityContainer().getEntity().getType())) {
				break;
			}
			
			// We want the value that we have already peeked from the iterator, so remove it from the iterator.
			sourceIterator.next();
			
			// Add the change to the result list.
			changeList.add(tmpChangeContainer);
		}
		
		return changeList;
	}


	/**
	 * {@inheritDoc}
	 */
	@Override
	public void remove() {
		throw new UnsupportedOperationException();
	}


	/**
	 * {@inheritDoc}
	 */
	@Override
	public void close() {
		sourceIterator.close();
	}
}
