/*
 *  Licensed to GIScience Research Group, Heidelberg University (GIScience)
 *
 *   	 http://www.giscience.uni-hd.de
 *   	 http://www.heigit.org
 *
 *  under one or more contributor license agreements. See the NOTICE file 
 *  distributed with this work for additional information regarding copyright 
 *  ownership. The GIScience licenses this file to you under the Apache License, 
 *  Version 2.0 (the "License"); you may not use this file except in compliance 
 *  with the License. You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package heigit.osmpoiextractor;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
import org.openstreetmap.osmosis.core.task.v0_6.Sink;
import org.openstreetmap.osmosis.pbf2.v0_6.PbfReader;

public class OSMInputFile implements Sink, Closeable
{
	private File _file;
	private final BlockingQueue<Entity> _entityQueue;
	private boolean hasIncomingData;
	private int workerThreads = -1;
	private boolean eof;

	public OSMInputFile( File file ) throws IOException
	{
		this._file = file;
		_entityQueue = new LinkedBlockingQueue<Entity>(50000);
	}

	public OSMInputFile open() 
	{
		openPBFReader(_file);
		return this;
	}

	public OSMInputFile setWorkerThreads( int num )
	{
		workerThreads = num;
		return this;
	}


	public Entity getNext()
	{
		if (eof)
			throw new IllegalStateException("EOF reached");

		Entity item = getNextPBF();

		if (item != null)
			return item;

		eof = true;
		return null;
	}

	public boolean isEOF()
	{
		return eof;
	}

	public void close() throws IOException
	{
		try
		{
			eof = true;

			if (pbfReaderThread != null && pbfReaderThread.isAlive())
				pbfReaderThread.interrupt();
		} catch (Exception ex)
		{
			throw new IOException(ex);
		} finally
		{
		}
	}

	Thread pbfReaderThread;

	private void openPBFReader(File file)
	{
		hasIncomingData = true;
		if (workerThreads <= 0)
			workerThreads = 2;

		PbfReader reader = new PbfReader(file, workerThreads);
		reader.setSink(this);
		pbfReaderThread = new Thread(reader, "PBF Reader");
		pbfReaderThread.start();
	}

	public void process(EntityContainer container) 
	{
		try
		{
			Entity entity = container.getEntity();
			EntityType type = entity.getType();
			if (type == EntityType.Node || type == EntityType.Way || type == EntityType.Relation)
			{
				_entityQueue.put(entity);
			}
		} 
		catch (InterruptedException ex)
		{
			throw new RuntimeException(ex);
		}
	}

	public void complete()
	{
		hasIncomingData = false;
	}

	private Entity getNextPBF()
	{
		Entity next = null;
		while (next == null)
		{
			if (!hasIncomingData && _entityQueue.isEmpty())
			{
				// we are done, stop polling
				eof = true;
				break;
			}

			try
			{
				// we cannot use "itemQueue.take()" as it blocks and hasIncomingData can change
				next = _entityQueue.poll(10, TimeUnit.MILLISECONDS);
			} catch (InterruptedException ex)
			{
				eof = true;
				break;
			}
		}

		return next;
	}

	public void initialize(Map<String, Object> metaData) {
		// TODO Auto-generated method stub

	}

	public void release() {
		// TODO Auto-generated method stub

	}
}