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

import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
import org.openstreetmap.osmosis.core.container.v0_6.EntityContainerFactory;
import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
import org.openstreetmap.osmosis.core.task.common.ChangeAction;


/**
 * Creates change records based on the data provided by an underlying entity history iterator.
 * 
 * @param <T>
 *            The type of entity provided by this iterator.
 */
public class ChangeReader<T extends Entity> implements ReleasableIterator<ChangeContainer> {

	private ReleasableIterator<EntityHistory<T>> source;
	private EntityContainerFactory<T> containerFactory;


	/**
	 * Creates a new instance.
	 * 
	 * @param source
	 *            The entity history source.
	 * @param containerFactory
	 *            The factory for wrapping entity objects into containers.
	 */
	public ChangeReader(ReleasableIterator<EntityHistory<T>> source, EntityContainerFactory<T> containerFactory) {
		this.source = source;
		this.containerFactory = containerFactory;
	}


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


	/**
	 * {@inheritDoc}
	 */
	@Override
	public ChangeContainer next() {
		EntityHistory<T> entityHistory;
		T entity;
		EntityContainer entityContainer;
		boolean createdPreviously;
		
		// Get the entity from the underlying source.
		entityHistory = source.next();
		entity = entityHistory.getEntity();
		
		// Wrap the entity in a container.
		entityContainer = containerFactory.createContainer(entity);
		
		// This is only a create if the version is 1.
		createdPreviously = (entityHistory.getEntity().getVersion() > 1);
		
		// The entity has been modified if it is visible and was created previously.
		// It is a create if it is visible and was NOT created previously.
		// It is a delete if it is NOT visible and was created previously.
		// No action if it is NOT visible and was NOT created previously.
		if (entityHistory.isVisible() && createdPreviously) {
			return new ChangeContainer(entityContainer, ChangeAction.Modify);
		} else if (entityHistory.isVisible() && !createdPreviously) {
			return new ChangeContainer(entityContainer, ChangeAction.Create);
		} else if (!entityHistory.isVisible() && createdPreviously) {
			return new ChangeContainer(entityContainer, ChangeAction.Delete);
		} else {
			// This is an unusual case in that an initial version has been marked as not visible.
			// The production database contains many examples of this, presumably due to the original
			// TIGER import not being deleted properly.
			return new ChangeContainer(entityContainer, ChangeAction.Delete);
		}
	}


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


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